{"components":{"securitySchemes":{"ApiKeyAuth":{"description":"A workspace key from Settings \u2192 API keys.","scheme":"bearer","type":"http"}}},"info":{"description":"Everything here happens inside the workspace the key belongs to; a key cannot reach another workspace.\n\nSend the key as `Authorization: Bearer sav_live_\u2026` (or `X-API-Key`). Keys are made in Settings \u2192 API keys and shown once \u2014 we keep only a hash, so a lost key is replaced, not recovered.\n\nRate limits are per key and answer `429` with `Retry-After`. Errors are JSON with an `error` field written for a person to read.","title":"Merkoria API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/analytics/summary":{"get":{"parameters":[{"in":"query","name":"days","schema":{"default":7,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"conversations_new":37,"conversations_open":12,"conversations_resolved":41,"days":7,"messages_in":412,"messages_out":508,"orders_paid":9,"revenue":[{"amount":18400.0,"currency":"UAH"}],"waiting_now":3},"schema":{"type":"object"}}},"description":"Messages, conversations, who is waiting, money that arrived"}},"summary":"A few numbers for another dashboard"}},"/contacts":{"get":{"description":"By exact phone (`phone=`) or by a search over name, phone and custom fields (`q=`). For a CRM sync, `updated_since=` returns cards changed after that moment, oldest first: keep the last `updated_at` you received and pass it next time.","parameters":[{"in":"query","name":"phone","schema":{"type":"string"}},{"in":"query","name":"q","schema":{"type":"string"}},{"in":"query","name":"updated_since","schema":{"format":"date-time","type":"string"}},{"description":"How many rows at most.","in":"query","name":"limit","schema":{"default":50,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The matching contacts"},"400":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"`updated_since` is not an ISO 8601 time"}},"summary":"Find contacts"},"post":{"description":"Matched by phone. Custom field names must be letters, digits and underscores.","requestBody":{"content":{"application/json":{"example":{"fields":{"city":"Kyiv"},"name":"Olena","phone":"380501234567","tags":["vip"]},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Updated"},"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Created"}},"summary":"Create or update a contact"}},"/contacts/{phone}/deal":{"patch":{"description":"Stage, amount and currency of the deal on the card. Moving it to `won` counts the amount as revenue in analytics; a new stage also starts the scenarios that wait for it and the `deal.stage_changed` webhook.","parameters":[{"description":"The customer's phone number (or Instagram id) as it appears in the inbox.","in":"path","name":"phone","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"amount":1200,"currency":"UAH","stage":"won"},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The contact as it is now"},"400":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"A stage, amount or currency that does not fit"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No such contact"}},"summary":"Set the deal on a contact"}},"/contacts/{phone}/tags":{"post":{"parameters":[{"description":"The customer's phone number (or Instagram id) as it appears in the inbox.","in":"path","name":"phone","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"add":["vip"],"remove":["cold"]},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The contact as it is now"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No such contact"}},"summary":"Add or remove tags"}},"/contacts/{phone}/unsubscribe":{"post":{"description":"For a shop with its own opt-out checkbox: ticking it there has to mean something here too.","parameters":[{"description":"The customer's phone number (or Instagram id) as it appears in the inbox.","in":"path","name":"phone","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"unsubscribed":true},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The contact as it is now"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No such contact"}},"summary":"Opt the person out of campaigns (or back in)"}},"/conversations":{"get":{"parameters":[{"in":"query","name":"status","schema":{"enum":["open","resolved"],"type":"string"}},{"description":"Only the ones where the customer wrote last and nobody answered.","in":"query","name":"waiting","schema":{"type":"boolean"}},{"description":"How many rows at most.","in":"query","name":"limit","schema":{"default":50,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"items":[{"assigned_to":null,"channel":"whatsapp_business","last_inbound_at":"2026-09-28T09:12:00+00:00","name":"Olena","phone":"380501234567","resolved":false,"status":"open","tags":["vip"],"unread":true}]},"schema":{"type":"object"}}},"description":"Conversations, newest first"}},"summary":"The conversation list"}},"/conversations/{phone}/messages":{"get":{"parameters":[{"description":"The customer's phone number (or Instagram id) as it appears in the inbox.","in":"path","name":"phone","required":true,"schema":{"type":"string"}},{"description":"How many rows at most.","in":"query","name":"limit","schema":{"default":50,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Oldest first"}},"summary":"The messages of one conversation"}},"/conversations/{phone}/resolve":{"post":{"description":"A customer writing reopens it by itself, so closing early costs nothing.","parameters":[{"description":"The customer's phone number (or Instagram id) as it appears in the inbox.","in":"path","name":"phone","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"resolved":true},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The contact and its new state"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No such contact"}},"summary":"Close a conversation (or open it again)"}},"/hooks/scenario/{token}":{"post":{"description":"Your form, CRM or shop starts a scenario for one person. The address itself is the secret \u2014 anybody who has it can start the scenario, so renew it if it leaks. No API key needed, and none accepted instead.","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"phone":"380501234567","vars":{"order_id":"A-19"}},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Started, or skipped because the conditions did not match"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No scenario with that address"},"409":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The scenario is switched off"}},"security":[],"summary":"Start a scenario from outside"}},"/me":{"get":{"description":"The workspace the key belongs to and which channels are actually connected. The first call to make when an integration is silent.","responses":{"200":{"content":{"application/json":{"example":{"channels":["whatsapp_business","instagram"],"workspace":{"id":"\u2026","name":"Acme Coffee","plan":"pro"}},"schema":{"type":"object"}}},"description":"The workspace and its channels"}},"summary":"Whose key this is"}},"/messages":{"post":{"description":"A plain text inside the 24-hour window, or an approved WhatsApp template outside it. The channel defaults to the one the conversation already uses.","requestBody":{"content":{"application/json":{"example":{"text":"Your order is on its way","to":"380501234567"},"schema":{"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"channel":"whatsapp_business","id":"wamid.\u2026","to":"380501234567"},"schema":{"type":"object"}}},"description":"Sent"},"409":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The person unsubscribed and this was a template"},"422":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The channel refused it \u2014 the reason is in `error`"}},"summary":"Send a message"}},"/openapi.json":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The OpenAPI document"}},"security":[],"summary":"This description"}},"/orders":{"get":{"parameters":[{"in":"query","name":"status","schema":{"type":"string"}},{"in":"query","name":"phone","schema":{"type":"string"}},{"description":"How many rows at most.","in":"query","name":"limit","schema":{"default":50,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The orders"}},"summary":"Orders, newest first"},"post":{"description":"The order your shop already knows about, in the chat \u2014 so the operator sees it and the reports count it.","requestBody":{"content":{"application/json":{"example":{"currency":"UAH","items":[{"name":"Coffee 1kg","price":450,"qty":2}],"phone":"380501234567"},"schema":{"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Nothing to order"}},"summary":"Create an order"}},"/orders/{number}":{"get":{"parameters":[{"in":"path","name":"number","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The order"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No such order"}},"summary":"One order"},"patch":{"description":"The same move the Orders page makes: scenarios, the deal stage and the reports all follow. `notify` writes to the customer in the same breath.","parameters":[{"in":"path","name":"number","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"notify":"Thanks! Payment received.","status":"paid"},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The order, and why the customer was not told if so"},"400":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Unknown status"},"404":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"No such order"}},"summary":"Move an order's status"}}},"security":[{"ApiKeyAuth":[]}],"servers":[{"url":"https://merkoria.com/api/v1"}],"tags":[{"name":"v1"}]}
