[{"data":1,"prerenderedAt":271},["ShallowReactive",2],{"doc-\u002Fabout\u002Fchangelog-policy":3},{"id":4,"title":5,"body":6,"description":261,"edit":262,"extension":263,"meta":264,"navigation":265,"path":266,"seo":267,"stem":268,"vertical":262,"weight":269,"__hash__":270},"content\u002Fabout\u002Fchangelog-policy.md","API versioning and deprecation policy",{"type":7,"value":8,"toc":250},"minimark",[9,13,18,21,38,45,64,69,83,87,90,119,126,134,172,176,219,223,232,243],[10,11,12],"p",{},"The OpenSense API surface is small but it is the contract you write\nyour devices and integrations against. We commit to a deprecation\npolicy that is more conservative than what a startup typically does.",[14,15,17],"h2",{"id":16},"semantic-versioning","Semantic versioning",[10,19,20],{},"The API is versioned in the URL path:",[22,23,24,32],"ul",{},[25,26,27,31],"li",{},[28,29,30],"code",{},"\u002Fv1\u002F..."," — the current stable surface.",[25,33,34,37],{},[28,35,36],{},"\u002Fv2\u002F..."," — a future major version, when we ship one.",[10,39,40,41,44],{},"Changes inside ",[28,42,43],{},"v1",":",[22,46,47,54],{},[25,48,49,53],{},[50,51,52],"strong",{},"Additive only."," New endpoints, new optional fields on existing\nendpoints, new enum values whose absence preserves old behaviour.",[25,55,56,59,60,63],{},[50,57,58],{},"Never"," rename a field, change a type, or remove a value from an\nenum. Such changes go to ",[28,61,62],{},"\u002Fv2",".",[10,65,66,67,44],{},"Changes that bump to ",[28,68,62],{},[22,70,71,74,77,80],{},[25,72,73],{},"Removal of a field.",[25,75,76],{},"Type change of a field.",[25,78,79],{},"Different meaning of an enum value.",[25,81,82],{},"Different request semantics (e.g. a previously synchronous\nendpoint becomes asynchronous).",[14,84,86],{"id":85},"deprecation-window","Deprecation window",[10,88,89],{},"When a feature is deprecated:",[22,91,92,99,113,116],{},[25,93,94,95,98],{},"It continues to work for ",[50,96,97],{},"12 months"," from the announcement date.",[25,100,101,102,105,106,63],{},"The HTTP response includes a ",[28,103,104],{},"Deprecation"," header with the sunset\ndate, per\n",[107,108,112],"a",{"href":109,"rel":110},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc8594",[111],"nofollow","RFC 8594",[25,114,115],{},"An email is sent to all account holders who have used the deprecated\nfeature in the last 90 days. The email includes the migration path.",[25,117,118],{},"The changelog records the deprecation as a discrete entry.",[10,120,121,122,125],{},"After 12 months the feature is removed; calls return ",[28,123,124],{},"410 Gone"," with\na body explaining the replacement.",[14,127,129,130,133],{"id":128},"what-is-not-covered-by-the-policy","What is ",[50,131,132],{},"not"," covered by the policy",[22,135,136,146,152,166],{},[25,137,138,141,142,145],{},[50,139,140],{},"Internal data formats"," — the shape of the ",[28,143,144],{},"_payload.json"," files\nserved by the dashboard, the shape of the audit-log entries we store\ninternally, the cookie names — are not part of the public API and\ncan change at any time. The customer-visible features they back\n(the dashboard chart, the audit-log export) honour the same policy\nas everything else.",[25,147,148,151],{},[50,149,150],{},"Rate limits"," can be raised at any time. Lowering them requires a\n30-day notice in the same channel as deprecations.",[25,153,154,157,158,160,161,165],{},[50,155,156],{},"New error codes"," can appear at any time. Removing an error code\nis treated as a v2-level change. (You should branch on ",[28,159,28],{},"\ndefensively — see ",[107,162,164],{"href":163},"\u002Fapi\u002Ferrors","the error codes table",".)",[25,167,168,171],{},[50,169,170],{},"New required fields"," in optional features. We can require a new\nfield in a freshly-added feature; we cannot require a new field in\nan existing one without a v2 bump.",[14,173,175],{"id":174},"what-you-should-write-to-make-this-easy","What you should write to make this easy",[22,177,178,190,200,206],{},[25,179,180,189],{},[50,181,182,183,185,186,63],{},"Branch on ",[28,184,28],{},", not on ",[28,187,188],{},"message"," Messages are human; codes\nare machine.",[25,191,192,195,196,199],{},[50,193,194],{},"Tolerate unknown enum values."," If we add ",[28,197,198],{},"severity: \"info\"","\nnext year, your client should not crash on it.",[25,201,202,205],{},[50,203,204],{},"Ignore unknown JSON fields."," Standard advice; we add fields\nroutinely.",[25,207,208,214,215,218],{},[50,209,210,211,63],{},"Send ",[28,212,213],{},"User-Agent"," When we email about deprecations we filter\nby User-Agent prefix to scope the audience. If your User-Agent is\ngeneric Python ",[28,216,217],{},"requests",", you may not get the notice.",[14,220,222],{"id":221},"v1-and-the-foreseeable-future","v1 and the foreseeable future",[10,224,225,226,228,229,231],{},"We have no plan to ship ",[28,227,62],{}," before 2028. The data model is stable\nand we have not yet found a v1-design decision worth a forced\nmigration. We will ship ",[28,230,62],{}," when one of the following is true:",[22,233,234,237,240],{},[25,235,236],{},"A regulatory change requires a payload schema change that cannot be\nadditive.",[25,238,239],{},"A storage migration forces a measurement-format change.",[25,241,242],{},"We adopt a third-party standard (e.g. OCF or a similar IoT\nframework) that supersedes our hand-rolled schema.",[10,244,245,246,249],{},"Until then, treat ",[28,247,248],{},"\u002Fv1"," as forever.",{"title":251,"searchDepth":252,"depth":252,"links":253},"",3,[254,256,257,259,260],{"id":16,"depth":255,"text":17},2,{"id":85,"depth":255,"text":86},{"id":128,"depth":255,"text":258},"What is not covered by the policy",{"id":174,"depth":255,"text":175},{"id":221,"depth":255,"text":222},"How we change the API without breaking your code",null,"md",{},true,"\u002Fabout\u002Fchangelog-policy",{"title":5,"description":261},"about\u002Fchangelog-policy",980,"unN6VieU80iuZ3kpPbLYdoZK9_sVpNZdY8ct_0Yuq6A",1779022956432]