{"openapi":"3.1.0","info":{"title":"OneMRR API","version":"1.0.0","description":"Send customers, subscription events, billing records and product activity to OneMRR from your own systems.\n\nAuthenticate with a secret key from Settings → API keys: `Authorization: Bearer sk_live_…`. Keys belong to one account and are rate limited to 120 requests a minute; batch endpoints take up to 500 items per request.\n\nEvery write names a `source_id` (see `GET /ping`). A custom source takes everything; a Wix source takes billing records (USD) and subscription events, which extend its payout history; any source takes customer details.\n\nBilling records drive MRR. Events and billing records are applied within a minute (202 Accepted); customer details apply at once."},"servers":[{"url":"https://app.onemrr.dev/api/v1"}],"security":[{"secretKey":[]}],"tags":[{"name":"Tracking"},{"name":"Customers"},{"name":"Subscriptions"},{"name":"Billing"},{"name":"Activity"},{"name":"Account"}],"paths":{"/ping":{"get":{"tags":["Account"],"summary":"Check a key","description":"The key's account, its name, and the account's sources with their IDs.","responses":{"200":{"description":"The key works.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingResult"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/customers":{"post":{"tags":["Customers"],"summary":"Upsert a customer","description":"Creates or updates a customer by `external_id`. Fields you leave out are kept; metadata is merged. Contact details sent here survive every sync of the source.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string up to 255 characters. Retrying with the same key returns the first response (marked Idempotent-Replayed: true) for 24 hours. Writes are also idempotent by their own IDs.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerRequest"},"example":{"source_id":7,"external_id":"acme.myshopify.com","name":"Acme","email":"owner@acme.test"}}}},"responses":{"200":{"description":"Updated or unchanged.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerResult"}}}},"201":{"description":"Created.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerResult"}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such source in this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ID was already used with different content (nothing changed), or a request with this Idempotency-Key is still running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The source can't take this data, or the Idempotency-Key was used with a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/customers/batch":{"post":{"tags":["Customers"],"summary":"Upsert customers in bulk","description":"Up to 500 customers in one request, applied together.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string up to 255 characters. Retrying with the same key returns the first response (marked Idempotent-Replayed: true) for 24 hours. Writes are also idempotent by their own IDs.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerBatchRequest"},"example":{"source_id":7,"customers":[{"external_id":"acme.myshopify.com","email":"owner@acme.test"}]}}}},"responses":{"200":{"description":"Applied.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such source in this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ID was already used with different content (nothing changed), or a request with this Idempotency-Key is still running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The source can't take this data, or the Idempotency-Key was used with a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/subscription-events":{"post":{"tags":["Subscriptions"],"summary":"Record a subscription event","description":"Lifecycle steps: `started`, `changed`, `cancellation_scheduled` (with `paid_through`, the confirmed end of paid access), `cancellation_retracted` and `cancelled`. Cancellations sent here count as verified for cancellation feedback. `started` and `changed` set MRR only for subscriptions without billing records.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string up to 255 characters. Retrying with the same key returns the first response (marked Idempotent-Replayed: true) for 24 hours. Writes are also idempotent by their own IDs.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionEventRequest"},"example":{"source_id":12,"external_id":"wix_cancel_9f2c","customer_external_id":"3b1f0c2e-wix-instance","subscription_external_id":"wix_sub_3b1f0c2e-wix-instance","type":"cancellation_scheduled","occurred_at":"2026-10-02T09:30:00Z","paid_through":"2026-11-01T12:00:00Z"}}}},"responses":{"200":{"description":"Already recorded (unchanged).","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionEventResult"}}}},"202":{"description":"Recorded; MRR recalculates shortly.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionEventResult"}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such source in this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ID was already used with different content (nothing changed), or a request with this Idempotency-Key is still running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The source can't take this data, or the Idempotency-Key was used with a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/billing-records":{"post":{"tags":["Billing"],"summary":"Record a payment or refund","description":"One payment or refund, keyed by `transaction_id`. Payments carry their invoice, cycle and service period; a refund references the original charge's `invoice_id`. Same shape as Ticket Spot's ChartMogul billing records.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string up to 255 characters. Retrying with the same key returns the first response (marked Idempotent-Replayed: true) for 24 hours. Writes are also idempotent by their own IDs.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRecordRequest"},"example":{"source_id":12,"instance_id":"3b1f0c2e-wix-instance","invoice_id":"wix-invoice-id","transaction_id":"wix-payment-id","type":"payment","result":"successful","date":"2026-09-01T12:00:00Z","invoice_date":"2026-09-01T12:00:00Z","currency":"USD","amount_in_cents":29000,"invoice_amount_in_cents":29000,"line_type":"subscription","product_id":"featured","cycle":"YEARLY","service_period_start":"2026-09-01T12:00:00Z","service_period_end":"2027-09-01T12:00:00Z"}}}},"responses":{"200":{"description":"Already recorded (unchanged).","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRecordResult"}}}},"202":{"description":"Recorded; MRR recalculates shortly.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRecordResult"}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such source in this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ID was already used with different content (nothing changed), or a request with this Idempotency-Key is still running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The source can't take this data, or the Idempotency-Key was used with a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/billing-records/batch":{"post":{"tags":["Billing"],"summary":"Record payments and refunds in bulk","description":"Up to 500 records. Each is applied on its own: a conflicting record fails alone and is reported in `results`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string up to 255 characters. Retrying with the same key returns the first response (marked Idempotent-Replayed: true) for 24 hours. Writes are also idempotent by their own IDs.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingBatchRequest"},"example":{"source_id":12,"records":[{"instance_id":"3b1f0c2e-wix-instance","invoice_id":"wix-invoice-id","transaction_id":"wix-payment-id","type":"payment","result":"successful","date":"2026-09-01T12:00:00Z","invoice_date":"2026-09-01T12:00:00Z","currency":"USD","amount_in_cents":29000,"invoice_amount_in_cents":29000,"line_type":"subscription","product_id":"featured","cycle":"YEARLY","service_period_start":"2026-09-01T12:00:00Z","service_period_end":"2027-09-01T12:00:00Z"}]}}}},"responses":{"200":{"description":"Nothing new.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"202":{"description":"Recorded; MRR recalculates shortly.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such source in this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ID was already used with different content (nothing changed), or a request with this Idempotency-Key is still running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The source can't take this data, or the Idempotency-Key was used with a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/identify":{"post":{"tags":["Tracking"],"summary":"Identify an account","description":"Creates or updates the account your app knows (your `account_id`) and links it to the billing customers it pays through. Links are explicit: OneMRR never matches by email. A billing customer belongs to one account; claiming one that another account holds returns `conflict` for that link and changes nothing until someone resolves it in OneMRR. A link to a customer that hasn't synced yet returns `pending_customer` and applies once it appears. Works with keys limited to tracking. Server-side only: requests from a browser are refused.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentifyRequest"},"example":{"account_id":"merchant_123","name":"Lakeside Events","email":"owner@lakeside.test","links":[{"source":"shopify","external_customer_id":"lakeside.myshopify.com"}]}}}},"responses":{"200":{"description":"Updated or unchanged; see each link's status.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"identity"},"account_id":{"type":"string"},"status":{"enum":["created","updated","unchanged"]},"links":{"type":"array","items":{"type":"object","properties":{"source_id":{"type":["integer","null"]},"external_customer_id":{"type":"string"},"status":{"enum":["linked","unchanged","pending_customer","conflict","error"]},"linked_to":{"type":"string","description":"For a conflict: the account_id that holds this billing customer."},"error":{"type":"string"}}}}}}}}},"201":{"description":"Created.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"identity"},"account_id":{"type":"string"},"status":{"enum":["created","updated","unchanged"]},"links":{"type":"array","items":{"type":"object","properties":{"source_id":{"type":["integer","null"]},"external_customer_id":{"type":"string"},"status":{"enum":["linked","unchanged","pending_customer","conflict","error"]},"linked_to":{"type":"string","description":"For a conflict: the account_id that holds this billing customer."},"error":{"type":"string"}}}}}}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"A browser sent the request (secret keys must stay on a server), or the key can't use this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/identify/batch":{"post":{"tags":["Tracking"],"summary":"Identify accounts in bulk","description":"Up to 500 identities in one request, for a backfill.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentifyBatchRequest"}}}},"responses":{"200":{"description":"Applied; counts and per-identity results.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"A browser sent the request (secret keys must stay on a server), or the key can't use this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/track":{"post":{"tags":["Tracking"],"summary":"Track events","description":"Up to 500 events per request: `{ events: [{ id, account_id, event, timestamp }] }`, or one event on its own. OneMRR keeps only the event name and time, counted per account per day; any other field (properties, URLs, IPs) is refused. Events with an `id` seen in the last 7 days count once. Timestamps may be up to 90 days old, or 400 with `?import=1` for a historical import. Accounts you haven't identified yet are counted and reported as unmatched.","parameters":[{"name":"import","in":"query","required":false,"description":"1 to accept timestamps up to 400 days old.","schema":{"type":"string","enum":["1"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackRequest"},"example":{"events":[{"id":"0192f1d2-7c1e-7c3a-9b1e-5d6f7a8b9c0d","account_id":"merchant_123","event":"event_published","timestamp":"2026-10-17T09:30:00Z"}]}}}},"responses":{"200":{"description":"Every event was a duplicate.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"track_result"},"accepted":{"type":"integer"},"duplicates":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"reason":{"enum":["invalid","unknown_field","timestamp_out_of_range","event_limit"]},"error":{"type":"string"}}}}}}}}},"202":{"description":"Accepted. `rejected` lists events that weren't, by index.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"track_result"},"accepted":{"type":"integer"},"duplicates":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"reason":{"enum":["invalid","unknown_field","timestamp_out_of_range","event_limit"]},"error":{"type":"string"}}}}}}}}},"400":{"description":"Invalid request. `issues` lists each field that failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"A browser sent the request (secret keys must stay on a server), or the key can't use this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/tracking/status":{"get":{"tags":["Tracking"],"summary":"Tracking status","description":"When the last event arrived, event counts, and how many accounts and paying customers are linked. The SDK's verify() calls this.","responses":{"200":{"description":"Status.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Wait for Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute for this key."},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."},"Retry-After":{"schema":{"type":"integer"}}}}}}},"/activity":{"post":{"tags":["Activity"],"summary":"Send activity pings","description":"Usage pings from the OneMRR SDK or your server, to spot customers who went quiet. Authenticated by the account's publishable key (`pk_…` in `key`, `X-OneMRR-Key`, or `Authorization: Bearer pk_…`), which can only send pings. Accepts text/plain so `navigator.sendBeacon` works.","security":[{"publishableKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ActivityPing"},{"type":"object","required":["pings"],"properties":{"key":{"type":"string"},"pings":{"type":"array","maxItems":500,"items":{"$ref":"#/components/schemas/ActivityPing"}}}}]},"example":{"key":"pk_live_…","customer":"acme.myshopify.com","source":"shopify","event":"login"}}}},"responses":{"200":{"description":"Accepted."},"400":{"description":"Invalid ping."},"401":{"description":"Missing or unknown publishable key."},"429":{"description":"Rate limited."}}}}},"components":{"securitySchemes":{"secretKey":{"type":"http","scheme":"bearer","description":"A secret key, sk_live_…, from Settings → API keys. Keep it on your server."},"publishableKey":{"type":"apiKey","in":"header","name":"X-OneMRR-Key","description":"The publishable key, pk_…, from Settings → Activity tracking. Safe in a browser."}},"schemas":{"CustomerInput":{"$id":"#/components/schemas/CustomerInput","type":"object","properties":{"external_id":{"type":"string","minLength":1,"maxLength":255,"description":"The customer's ID in the source: Stripe customer ID, myshopify domain, Wix instance ID, or your own ID for a custom source.","example":"acme.myshopify.com"},"name":{"type":"string","minLength":1,"maxLength":255},"email":{"description":"Contact email, used for cancellation feedback. null clears it.","anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"country":{"anyOf":[{"type":"string","pattern":"^[A-Za-z]{2}$"},{"type":"null"}]},"metadata":{"type":"object","propertyNames":{"type":"string","maxLength":100},"additionalProperties":{"type":"string","maxLength":500},"description":"String key-value pairs, merged into the customer's existing metadata.","example":{"plan_tier":"premium"}}},"required":["external_id"]},"CustomerRequest":{"$id":"#/components/schemas/CustomerRequest","type":"object","properties":{"external_id":{"type":"string","minLength":1,"maxLength":255,"description":"The customer's ID in the source: Stripe customer ID, myshopify domain, Wix instance ID, or your own ID for a custom source.","example":"acme.myshopify.com"},"name":{"type":"string","minLength":1,"maxLength":255},"email":{"description":"Contact email, used for cancellation feedback. null clears it.","anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"country":{"anyOf":[{"type":"string","pattern":"^[A-Za-z]{2}$"},{"type":"null"}]},"metadata":{"type":"object","propertyNames":{"type":"string","maxLength":100},"additionalProperties":{"type":"string","maxLength":500},"description":"String key-value pairs, merged into the customer's existing metadata.","example":{"plan_tier":"premium"}},"source_id":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The source this data belongs to (Sources page, or GET /api/v1/ping).","example":12}},"required":["external_id","source_id"]},"CustomerBatchRequest":{"$id":"#/components/schemas/CustomerBatchRequest","type":"object","properties":{"source_id":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The source this data belongs to (Sources page, or GET /api/v1/ping).","example":12},"customers":{"minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/CustomerInput"}}},"required":["source_id","customers"]},"Price":{"$id":"#/components/schemas/Price","type":"object","properties":{"amount_cents":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Price per billing interval, before tax."},"currency":{"example":"USD","type":"string","pattern":"^[A-Za-z]{3}$"},"interval":{"type":"string","enum":["month","year"]},"interval_count":{"default":1,"type":"integer","minimum":1,"maximum":36}},"required":["amount_cents","interval"]},"SubscriptionEventRequest":{"$id":"#/components/schemas/SubscriptionEventRequest","type":"object","properties":{"source_id":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The source this data belongs to (Sources page, or GET /api/v1/ping).","example":12},"external_id":{"type":"string","minLength":1,"maxLength":255,"description":"Your unique ID for this event. Replaying it is safe.","example":"wix_cancel_9f2c…"},"customer_external_id":{"type":"string","minLength":1,"maxLength":255},"subscription_external_id":{"type":"string","minLength":1,"maxLength":255,"example":"wix_sub_3b1f…"},"type":{"type":"string","enum":["started","changed","cancellation_scheduled","cancellation_retracted","cancelled"],"description":"started and changed set the price (used for MRR only when the subscription has no billing records). cancellation_scheduled needs paid_through, when paid access ends. cancellation_retracted withdraws it. cancelled means access has ended."},"occurred_at":{"type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","description":"When it happened (for a scheduled cancellation: when the customer asked).","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"},"paid_through":{"description":"When paid access ends. Required for cancellation_scheduled; for cancelled, when access ended.","type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"},"plan":{"type":"object","properties":{"external_id":{"type":"string","minLength":1,"maxLength":255},"name":{"type":"string","minLength":1,"maxLength":255}},"required":["external_id"]},"price":{"$ref":"#/components/schemas/Price"},"quantity":{"type":"integer","minimum":1,"maximum":1000000}},"required":["source_id","external_id","customer_external_id","subscription_external_id","type","occurred_at"]},"BillingRecord":{"$id":"#/components/schemas/BillingRecord","type":"object","properties":{"customer_external_id":{"description":"The customer's ID in the source. instance_id is accepted as an alias (Wix).","type":"string","minLength":1,"maxLength":255},"instance_id":{"description":"Alias of customer_external_id for Wix records.","type":"string","minLength":1,"maxLength":255},"subscription_external_id":{"description":"Defaults to wix_sub_{customer} on a Wix source and to the customer's ID on a custom source.","type":"string","minLength":1,"maxLength":255},"invoice_id":{"type":"string","minLength":1,"maxLength":255},"transaction_id":{"type":"string","minLength":1,"maxLength":255,"description":"Unique per payment or refund attempt. Replaying the same transaction is safe."},"type":{"type":"string","enum":["payment","refund"]},"result":{"type":"string","enum":["successful","failed"]},"date":{"type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","description":"When the money moved.","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"},"currency":{"example":"USD","type":"string","pattern":"^[A-Za-z]{3}$"},"amount_in_cents":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Amount collected (payment) or returned (refund), positive."},"invoice_date":{"description":"Payments: when the invoice was issued.","type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"},"invoice_amount_in_cents":{"description":"Payments: the final invoiced amount after discounts, including tax.","type":"integer","minimum":0,"maximum":9007199254740991},"tax_amount_in_cents":{"type":"integer","minimum":0,"maximum":9007199254740991},"line_type":{"description":"Payments: one_time never counts toward MRR.","type":"string","enum":["subscription","one_time"]},"product_id":{"description":"Subscription payments: the plan or product.","type":"string","minLength":1,"maxLength":255},"cycle":{"type":"string","enum":["MONTHLY","YEARLY"]},"service_period_start":{"type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"},"service_period_end":{"type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"},"prorated":{"type":"boolean"},"proration_type":{"type":"string","enum":["differential","full"]},"description":{"type":"string","maxLength":500},"customer":{"description":"Optional contact details, applied like POST /customers.","type":"object","properties":{"name":{"type":"string","maxLength":255},"email":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}}}},"required":["invoice_id","transaction_id","type","result","date","currency","amount_in_cents"]},"BillingRecordRequest":{"$id":"#/components/schemas/BillingRecordRequest","allOf":[{"type":"object","properties":{"source_id":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The source this data belongs to (Sources page, or GET /api/v1/ping).","example":12}},"required":["source_id"]},{"$ref":"#/components/schemas/BillingRecord"}]},"BillingBatchRequest":{"$id":"#/components/schemas/BillingBatchRequest","type":"object","properties":{"source_id":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The source this data belongs to (Sources page, or GET /api/v1/ping).","example":12},"records":{"minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/BillingRecord"}}},"required":["source_id","records"]},"CustomerResult":{"$id":"#/components/schemas/CustomerResult","type":"object","properties":{"object":{"type":"string","const":"customer"},"source_id":{"type":"number"},"external_id":{"type":"string"},"status":{"type":"string","enum":["created","updated","unchanged"]}},"required":["object","source_id","external_id","status"]},"SubscriptionEventResult":{"$id":"#/components/schemas/SubscriptionEventResult","type":"object","properties":{"object":{"type":"string","const":"subscription_event"},"source_id":{"type":"number"},"external_id":{"type":"string"},"status":{"type":"string","enum":["created","unchanged"]}},"required":["object","source_id","external_id","status"]},"BillingRecordResult":{"$id":"#/components/schemas/BillingRecordResult","type":"object","properties":{"object":{"type":"string","const":"billing_record"},"source_id":{"type":"number"},"transaction_id":{"type":"string"},"status":{"type":"string","enum":["created","unchanged"]}},"required":["object","source_id","transaction_id","status"]},"BatchResult":{"$id":"#/components/schemas/BatchResult","type":"object","properties":{"object":{"type":"string","const":"batch"},"source_id":{"type":"number"},"counts":{"type":"object","properties":{"created":{"type":"number"},"updated":{"type":"number"},"unchanged":{"type":"number"},"failed":{"type":"number"}},"required":["created","updated","unchanged","failed"]},"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number"},"status":{"type":"string","enum":["created","updated","unchanged","failed"]},"error":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"}},"required":["type","message"]}},"required":["index","status"]}}},"required":["object","source_id","counts","results"]},"PingResult":{"$id":"#/components/schemas/PingResult","type":"object","properties":{"object":{"type":"string","const":"ping"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"key":{"type":"object","properties":{"name":{"type":"string"},"scope":{"type":"string","enum":["all","tracking"]}},"required":["name","scope"]},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"kind":{"type":"string"},"name":{"type":"string"}},"required":["id","kind","name"]}}},"required":["object","account","key","sources"]},"Error":{"$id":"#/components/schemas/Error","type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["authentication_error","permission_error","not_found","invalid_request","conflict","idempotency_error","rate_limited","api_error"]},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["type","message"]}},"required":["error"]},"IdentityLink":{"$id":"#/components/schemas/IdentityLink","type":"object","properties":{"source_id":{"description":"The billing source (Sources page or GET /ping). Required when you have more than one source of a kind.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"source":{"description":"The billing source kind, when you have one source of that kind.","type":"string","enum":["stripe","shopify","wix","paddle","lemonsqueezy","csv","custom"]},"external_customer_id":{"type":"string","minLength":1,"maxLength":255,"description":"The customer's ID in that source: Stripe customer ID, myshopify domain, Wix instance ID, or your custom source's ID.","example":"shop-123.myshopify.com"}},"required":["external_customer_id"],"additionalProperties":false},"IdentifyRequest":{"$id":"#/components/schemas/IdentifyRequest","type":"object","properties":{"account_id":{"type":"string","minLength":1,"maxLength":200,"description":"Your own ID for the customer account (a workspace, team or merchant), as your app knows it.","example":"merchant_123"},"name":{"description":"A display name, shown when the account isn't linked yet.","anyOf":[{"type":"string","minLength":1,"maxLength":255},{"type":"null"}]},"email":{"description":"Optional contact email. Used only for retention emails when the billing source has none; null clears it.","anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"links":{"description":"Billing customers this account pays through. A billing customer can belong to one account; a second claim is reported as a conflict.","maxItems":10,"type":"array","items":{"$ref":"#/components/schemas/IdentityLink"}},"source_id":{"description":"Shorthand for one link.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"source":{"description":"Shorthand for one link.","type":"string","enum":["stripe","shopify","wix","paddle","lemonsqueezy","csv","custom"]},"external_customer_id":{"description":"Shorthand for one link.","type":"string","minLength":1,"maxLength":255}},"required":["account_id"],"additionalProperties":false},"IdentifyBatchRequest":{"$id":"#/components/schemas/IdentifyBatchRequest","type":"object","properties":{"identities":{"minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/IdentifyRequest"}}},"required":["identities"],"additionalProperties":false},"TrackEvent":{"$id":"#/components/schemas/TrackEvent","type":"object","properties":{"id":{"description":"A unique ID for this event (the SDK sends a UUID). Retries with the same ID are counted once, for 7 days.","example":"0192f1d2-7c1e-7c3a-9b1e-5d6f7a8b9c0d","type":"string","minLength":1,"maxLength":100,"pattern":"^[\\x21-\\x7e]+$"},"account_id":{"type":"string","minLength":1,"maxLength":200,"description":"Your own ID for the customer account (a workspace, team or merchant), as your app knows it.","example":"merchant_123"},"event":{"example":"event_published","type":"string","pattern":"^[a-z0-9][a-z0-9_.:-]{0,63}$"},"timestamp":{"description":"When it happened. Defaults to now. Up to 90 days back (400 with ?import=1).","type":"string","format":"date-time","example":"2026-09-01T12:00:00Z","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?(Z|[+-]\\d{2}:?\\d{2})?)?$"}},"required":["account_id","event"],"additionalProperties":false},"TrackRequest":{"$id":"#/components/schemas/TrackRequest","type":"object","properties":{"events":{"minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/TrackEvent"}}},"required":["events"],"additionalProperties":false},"ActivityPing":{"type":"object","required":["customer"],"properties":{"key":{"type":"string","description":"Publishable key, if not sent in a header."},"customer":{"type":"string","description":"The customer's ID in the billing source: Stripe customer ID, myshopify domain, Wix instance ID."},"source":{"type":"string","enum":["stripe","shopify","wix","paddle","lemonsqueezy","csv"]},"user":{"type":"string","description":"The person using the app; hashed before storage."},"event":{"type":"string","description":"A short event name such as \"login\". Defaults to \"ping\"."},"hash":{"type":"string","description":"HMAC-SHA256(identity secret, customer), hex, from your server."},"at":{"type":"string","format":"date-time","description":"When it happened; server pings may backdate up to 90 days."}}}}}}