[{"data":1,"prerenderedAt":506},["ShallowReactive",2],{"doc-\u002Fapi\u002Fingest-mqtt":3},{"id":4,"title":5,"body":6,"description":496,"edit":497,"extension":498,"meta":499,"navigation":500,"path":501,"seo":502,"stem":503,"vertical":497,"weight":504,"__hash__":505},"content\u002Fapi\u002Fingest-mqtt.md","MQTT ingest",{"type":7,"value":8,"toc":487},"minimark",[9,19,24,31,44,48,56,59,109,112,144,155,159,167,276,291,295,365,368,372,420,423,426,456,459,463,470,473,477,480,483],[10,11,12,13,18],"p",{},"OpenSense exposes a multi-tenant MQTT 5 bridge for customers who run\ntheir own broker on-premises. See the\n",[14,15,17],"a",{"href":16},"\u002Fhardware\u002Fdiy-esp32-mqtt","DIY ESP32 + MQTT"," page for the device-side\nguide. This page is the topic \u002F payload \u002F QoS contract.",[20,21,23],"h2",{"id":22},"bridge-endpoint","Bridge endpoint",[25,26,28],"cmd",{"label":27},"$ bridge url",[10,29,30],{},"mqtts:\u002F\u002Fmqtt.opensense.murzin.digital:8883",[32,33,34,38,41],"ul",{},[35,36,37],"li",{},"TLS 1.2 or 1.3, mTLS required.",[35,39,40],{},"Cert + key + CA bundle is downloaded from the dashboard when you add\nan MQTT bridge.",[35,42,43],{},"MQTT 5 only. MQTT 3.1.1 is rejected at handshake.",[20,45,47],{"id":46},"topics","Topics",[10,49,50,51,55],{},"Each account has a dedicated topic root: ",[52,53,54],"code",{},"opensense\u002F\u003Caccount_id>\u002F",".",[10,57,58],{},"Publish topics (your broker → our broker):",[60,61,62,75],"table",{},[63,64,65],"thead",{},[66,67,68,72],"tr",{},[69,70,71],"th",{},"Topic",[69,73,74],{},"Purpose",[76,77,78,89,99],"tbody",{},[66,79,80,86],{},[81,82,83],"td",{},[52,84,85],{},"opensense\u002F\u003Cacc>\u002F\u003Cdevice_id>",[81,87,88],{},"Measurement payload (JSON)",[66,90,91,96],{},[81,92,93],{},[52,94,95],{},"opensense\u002F\u003Cacc>\u002F\u003Cdevice_id>\u002Flwt",[81,97,98],{},"Last-will \u002F online-status payload",[66,100,101,106],{},[81,102,103],{},[52,104,105],{},"opensense\u002F\u003Cacc>\u002F\u003Cdevice_id>\u002Finfo",[81,107,108],{},"Firmware version, model (one-shot)",[10,110,111],{},"Subscribe topics (our broker → your broker, optional):",[60,113,114,122],{},[63,115,116],{},[66,117,118,120],{},[69,119,71],{},[69,121,74],{},[76,123,124,134],{},[66,125,126,131],{},[81,127,128],{},[52,129,130],{},"opensense\u002F\u003Cacc>\u002F\u003Cdevice_id>\u002Fcmd\u002Fcadence",[81,132,133],{},"Server-side request to change cadence",[66,135,136,141],{},[81,137,138],{},[52,139,140],{},"opensense\u002F\u003Cacc>\u002F\u003Cdevice_id>\u002Fcmd\u002Fidentify",[81,142,143],{},"\"Make the LED blink\" — for finding it",[10,145,146,147,150,151,154],{},"The ",[52,148,149],{},"cmd\u002F*"," topics are placeholders today; only ",[52,152,153],{},"cmd\u002Fidentify"," is\nimplemented. We may extend later.",[20,156,158],{"id":157},"measurement-payload","Measurement payload",[10,160,161,162,166],{},"Same as ",[14,163,165],{"href":164},"\u002Fapi\u002Fingest","POST \u002Fv1\u002Fingest",":",[168,169,174],"pre",{"className":170,"code":171,"language":172,"meta":173,"style":173},"language-json shiki shiki-themes github-dark github-dark","{\n  \"ts\": \"2026-05-17T08:22:00Z\",\n  \"measurements\": [\n    { \"type\": \"temperature\", \"value\": 4.2 },\n    { \"type\": \"humidity\",    \"value\": 64.1 }\n  ]\n}\n","json","",[52,175,176,185,202,211,239,264,270],{"__ignoreMap":173},[177,178,181],"span",{"class":179,"line":180},"line",1,[177,182,184],{"class":183},"suv1-","{\n",[177,186,188,192,195,199],{"class":179,"line":187},2,[177,189,191],{"class":190},"s8ozJ","  \"ts\"",[177,193,194],{"class":183},": ",[177,196,198],{"class":197},"s4wv1","\"2026-05-17T08:22:00Z\"",[177,200,201],{"class":183},",\n",[177,203,205,208],{"class":179,"line":204},3,[177,206,207],{"class":190},"  \"measurements\"",[177,209,210],{"class":183},": [\n",[177,212,214,217,220,222,225,228,231,233,236],{"class":179,"line":213},4,[177,215,216],{"class":183},"    { ",[177,218,219],{"class":190},"\"type\"",[177,221,194],{"class":183},[177,223,224],{"class":197},"\"temperature\"",[177,226,227],{"class":183},", ",[177,229,230],{"class":190},"\"value\"",[177,232,194],{"class":183},[177,234,235],{"class":190},"4.2",[177,237,238],{"class":183}," },\n",[177,240,242,244,246,248,251,254,256,258,261],{"class":179,"line":241},5,[177,243,216],{"class":183},[177,245,219],{"class":190},[177,247,194],{"class":183},[177,249,250],{"class":197},"\"humidity\"",[177,252,253],{"class":183},",    ",[177,255,230],{"class":190},[177,257,194],{"class":183},[177,259,260],{"class":190},"64.1",[177,262,263],{"class":183}," }\n",[177,265,267],{"class":179,"line":266},6,[177,268,269],{"class":183},"  ]\n",[177,271,273],{"class":179,"line":272},7,[177,274,275],{"class":183},"}\n",[10,277,278,281,282,286,287,290],{},[52,279,280],{},"device"," field is ",[283,284,285],"strong",{},"omitted"," — the device id is the second path segment\nof the topic. ",[52,288,289],{},"ts"," is optional; if omitted, server-receive time is used.",[20,292,294],{"id":293},"qos","QoS",[60,296,297,312],{},[63,298,299],{},[66,300,301,303,306,309],{},[69,302,71],{},[69,304,305],{},"Required QoS",[69,307,308],{},"Retain",[69,310,311],{},"Why",[76,313,314,328,341,353],{},[66,315,316,319,322,325],{},[81,317,318],{},"Measurement",[81,320,321],{},"1",[81,323,324],{},"false",[81,326,327],{},"Stream, not state",[66,329,330,333,335,338],{},[81,331,332],{},"LWT (last-will)",[81,334,321],{},[81,336,337],{},"true",[81,339,340],{},"Survive broker restarts",[66,342,343,346,348,350],{},[81,344,345],{},"Info",[81,347,321],{},[81,349,337],{},[81,351,352],{},"One-shot retained presence",[66,354,355,358,360,362],{},[81,356,357],{},"Cmd (downlink)",[81,359,321],{},[81,361,324],{},[81,363,364],{},"Single-shot",[10,366,367],{},"QoS 0 is rejected (silent drop) for measurement topics — the bridge\nwill not let you race the network for free. QoS 2 is accepted but\ndowngraded to 1 at the ingest point; the storage layer dedups by\ncontent anyway.",[20,369,371],{"id":370},"last-will-format","Last-will format",[168,373,375],{"className":170,"code":374,"language":172,"meta":173,"style":173},"{\n  \"status\": \"offline\",\n  \"reason\": \"device-disconnected\",\n  \"at\":     \"2026-05-17T08:22:00Z\"\n}\n",[52,376,377,381,393,405,416],{"__ignoreMap":173},[177,378,379],{"class":179,"line":180},[177,380,184],{"class":183},[177,382,383,386,388,391],{"class":179,"line":187},[177,384,385],{"class":190},"  \"status\"",[177,387,194],{"class":183},[177,389,390],{"class":197},"\"offline\"",[177,392,201],{"class":183},[177,394,395,398,400,403],{"class":179,"line":204},[177,396,397],{"class":190},"  \"reason\"",[177,399,194],{"class":183},[177,401,402],{"class":197},"\"device-disconnected\"",[177,404,201],{"class":183},[177,406,407,410,413],{"class":179,"line":213},[177,408,409],{"class":190},"  \"at\"",[177,411,412],{"class":183},":     ",[177,414,415],{"class":197},"\"2026-05-17T08:22:00Z\"\n",[177,417,418],{"class":179,"line":241},[177,419,275],{"class":183},[10,421,422],{},"Set as the LWT of your device's MQTT client when connecting to your\nlocal broker. Forward via bridge with retain=true. When the local\nbroker promotes the LWT (TCP close on the device), it propagates\nthrough the bridge and OpenSense flips the device's connection state.",[10,424,425],{},"On reconnect, the device publishes:",[168,427,429],{"className":170,"code":428,"language":172,"meta":173,"style":173},"{ \"status\": \"online\", \"at\": \"2026-05-17T08:23:00Z\" }\n",[52,430,431],{"__ignoreMap":173},[177,432,433,436,439,441,444,446,449,451,454],{"class":179,"line":180},[177,434,435],{"class":183},"{ ",[177,437,438],{"class":190},"\"status\"",[177,440,194],{"class":183},[177,442,443],{"class":197},"\"online\"",[177,445,227],{"class":183},[177,447,448],{"class":190},"\"at\"",[177,450,194],{"class":183},[177,452,453],{"class":197},"\"2026-05-17T08:23:00Z\"",[177,455,263],{"class":183},[10,457,458],{},"…and the connection state flips back.",[20,460,462],{"id":461},"authentication","Authentication",[10,464,465,466,469],{},"mTLS only. The client cert subject CN identifies the ",[283,467,468],{},"account","; the\ntopic prefix is enforced on the bridge side against that subject. A\nclient cert for account A cannot publish to account B's topic tree\neven if it tries.",[10,471,472],{},"There is no password-based auth on the MQTT bridge. If you cannot do\nmTLS, use HTTPS ingest instead — we are not going to weaken MQTT\nmulti-tenancy to ease setup.",[20,474,476],{"id":475},"why-bridge-not-direct-device-to-opensense-mqtt","Why bridge, not direct device-to-OpenSense MQTT?",[10,478,479],{},"Direct device connections are a Team-tier feature. Solo is priced for\nHTTP-first integrations and the on-prem MQTT bridge is the workaround\nfor customers who cannot do outbound HTTPS but can do MQTT.",[10,481,482],{},"For scale (>100 devices), revisit: at that scale you want direct\nsessions on our broker, which means individual device certificates,\nwhich means provisioning workflow — none of which is free.",[484,485,486],"style",{},"html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":173,"searchDepth":204,"depth":204,"links":488},[489,490,491,492,493,494,495],{"id":22,"depth":187,"text":23},{"id":46,"depth":187,"text":47},{"id":157,"depth":187,"text":158},{"id":293,"depth":187,"text":294},{"id":370,"depth":187,"text":371},{"id":461,"depth":187,"text":462},{"id":475,"depth":187,"text":476},"Topics, payload, QoS, last-will",null,"md",{},true,"\u002Fapi\u002Fingest-mqtt",{"title":5,"description":496},"api\u002Fingest-mqtt",230,"cktve4g1UykLLT24STEBmK3ZZSx64vvslnRJ6EFQ7ak",1779022954016]