{"openapi":"3.1.0","info":{"title":"Public API","version":"2026-10-06","description":"Send `API-Version: <date>` (one of 2026-10-06) to pin a version; without it a request gets the current one, 2026-10-06. Every answer names its version in `API-Version`; an unknown one is `badRequest`. A change that could break a client ships under a new date, and older dates keep working for at least 12 months."},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An org's API key, `<id>.<secret>`, created by its owners and admins."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]}}},"paths":{"/members":{"get":{"summary":"The org's members.","description":"Needs an API key with the `read` scope. A page, oldest first. Pass `next` as `after` for the items following it: on the last page it is still the last item's cursor (on an empty page, the `after` given), so a poller resumes from it; `hasMore` says whether more follow now. An item's time is set just before it is saved, so one saved slowly can land behind a cursor already handed out.","security":[{"apiKey":["read"]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"after","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,15}\\.[\\s\\S]+$"}}],"responses":{"200":{"description":"The org's members.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"}},"required":["id","userId","role","createdAt"]}},"next":{"anyOf":[{"type":"string","pattern":"^\\d{1,15}\\.[\\s\\S]+$"},{"type":"null"}]},"hasMore":{"type":"boolean"}},"required":["data","next","hasMore"]}}}},"400":{"description":"Invalid query (`badRequest`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown, wrong, revoked, or expired key, or one whose creator may no longer manage keys (`unauthorized`), with `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The org's plan doesn't include the API or this route (`upgradeRequired`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`forbidden`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the rate limit of the client or the key (`rateLimited`), with `Retry-After` in seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audit-log":{"get":{"summary":"The org's audit log.","description":"Needs an API key with the `read` scope. A page, oldest first. Pass `next` as `after` for the items following it: on the last page it is still the last item's cursor (on an empty page, the `after` given), so a poller resumes from it; `hasMore` says whether more follow now. An item's time is set just before it is saved, so one saved slowly can land behind a cursor already handed out.","security":[{"apiKey":["read"]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"after","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,15}\\.[\\s\\S]+$"}}],"responses":{"200":{"description":"The org's audit log.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actorId":{"type":["string","null"]},"action":{"type":"string"},"targetId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"}},"required":["id","actorId","action","targetId","createdAt"]}},"next":{"anyOf":[{"type":"string","pattern":"^\\d{1,15}\\.[\\s\\S]+$"},{"type":"null"}]},"hasMore":{"type":"boolean"}},"required":["data","next","hasMore"]}}}},"400":{"description":"Invalid query (`badRequest`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown, wrong, revoked, or expired key, or one whose creator may no longer manage keys (`unauthorized`), with `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The org's plan doesn't include the API or this route (`upgradeRequired`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`forbidden`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the rate limit of the client or the key (`rateLimited`), with `Retry-After` in seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/test":{"post":{"summary":"Sends a test event to one of the org's webhook endpoints.","description":"Needs an API key with the `write` scope. Send an `Idempotency-Key` (1 to 255 printable ASCII characters) to make a retry safe: for a day, the same key with the same request gets the first answer again (with `Idempotent-Replayed: true`), and with another request `idempotencyMismatch` (422).","security":[{"apiKey":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}}],"responses":{"200":{"description":"Sends a test event to one of the org's webhook endpoints.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"401":{"description":"Missing, malformed, unknown, wrong, revoked, or expired key, or one whose creator may no longer manage keys (`unauthorized`), with `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The org's plan doesn't include the API or this route (`upgradeRequired`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`forbidden`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such endpoint in the org (`notFound`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The endpoint is disabled, or a request with this `Idempotency-Key` is still running (`conflict`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The `Idempotency-Key` was used for another request (`idempotencyMismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the rate limit of the client or the key (`rateLimited`), with `Retry-After` in seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/assistant/instructions":{"get":{"summary":"The org's instructions to the assistant, null when it set none.","description":"Needs an API key with the `read` scope.","security":[{"apiKey":["read"]}],"parameters":[],"responses":{"200":{"description":"The org's instructions to the assistant, null when it set none.","content":{"application/json":{"schema":{"type":"object","properties":{"instructions":{"type":["string","null"]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}]}},"required":["instructions","updatedAt"]}}}},"401":{"description":"Missing, malformed, unknown, wrong, revoked, or expired key, or one whose creator may no longer manage keys (`unauthorized`), with `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The org's plan doesn't include the API or this route (`upgradeRequired`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`forbidden`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over the rate limit of the client or the key (`rateLimited`), with `Retry-After` in seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"member.added":{"post":{"summary":"A member joins","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The delivery's ID, the same on every retry and replay: skip one already handled.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"When this attempt was signed, in Unix seconds: refuse one more than 5 minutes off.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,` and the base64 HMAC-SHA256, under the endpoint secret's bytes (base64 after `whsec_`), of `<webhook-id>.<webhook-timestamp>.<body>`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","const":"member.added"},"timestamp":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"data":{"type":"object","properties":{"orgId":{"type":"string"},"memberId":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string"}},"required":["orgId","memberId","userId","role"]}},"required":["type","timestamp","data"]}}}},"responses":{"200":{"description":"Any 2xx within 15 seconds acknowledges it; anything else is retried after 1, 5, 25, 125, and 625 minutes."}}}},"member.removed":{"post":{"summary":"A member leaves or is removed","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The delivery's ID, the same on every retry and replay: skip one already handled.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"When this attempt was signed, in Unix seconds: refuse one more than 5 minutes off.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,` and the base64 HMAC-SHA256, under the endpoint secret's bytes (base64 after `whsec_`), of `<webhook-id>.<webhook-timestamp>.<body>`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","const":"member.removed"},"timestamp":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"data":{"type":"object","properties":{"orgId":{"type":"string"},"memberId":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string"}},"required":["orgId","memberId","userId","role"]}},"required":["type","timestamp","data"]}}}},"responses":{"200":{"description":"Any 2xx within 15 seconds acknowledges it; anything else is retried after 1, 5, 25, 125, and 625 minutes."}}}},"subscription.changed":{"post":{"summary":"The subscription changes","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The delivery's ID, the same on every retry and replay: skip one already handled.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"When this attempt was signed, in Unix seconds: refuse one more than 5 minutes off.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,` and the base64 HMAC-SHA256, under the endpoint secret's bytes (base64 after `whsec_`), of `<webhook-id>.<webhook-timestamp>.<body>`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","const":"subscription.changed"},"timestamp":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"data":{"type":"object","properties":{"orgId":{"type":"string"},"subscriptionId":{"type":"string"},"product":{"type":"string"},"plan":{"type":"string"},"interval":{"type":"string"},"status":{"type":"string"}},"required":["orgId","subscriptionId","product","plan","interval","status"]}},"required":["type","timestamp","data"]}}}},"responses":{"200":{"description":"Any 2xx within 15 seconds acknowledges it; anything else is retried after 1, 5, 25, 125, and 625 minutes."}}}},"file.uploaded":{"post":{"summary":"A file is uploaded","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The delivery's ID, the same on every retry and replay: skip one already handled.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"When this attempt was signed, in Unix seconds: refuse one more than 5 minutes off.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,` and the base64 HMAC-SHA256, under the endpoint secret's bytes (base64 after `whsec_`), of `<webhook-id>.<webhook-timestamp>.<body>`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","const":"file.uploaded"},"timestamp":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"data":{"type":"object","properties":{"orgId":{"type":"string"},"fileId":{"type":"string"},"name":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"number"}},"required":["orgId","fileId","name","contentType","size"]}},"required":["type","timestamp","data"]}}}},"responses":{"200":{"description":"Any 2xx within 15 seconds acknowledges it; anything else is retried after 1, 5, 25, 125, and 625 minutes."}}}},"assistant.instructionsChanged":{"post":{"summary":"The assistant's instructions change","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The delivery's ID, the same on every retry and replay: skip one already handled.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"When this attempt was signed, in Unix seconds: refuse one more than 5 minutes off.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,` and the base64 HMAC-SHA256, under the endpoint secret's bytes (base64 after `whsec_`), of `<webhook-id>.<webhook-timestamp>.<body>`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","const":"assistant.instructionsChanged"},"timestamp":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"data":{"type":"object","properties":{"orgId":{"type":"string"}},"required":["orgId"],"additionalProperties":{}}},"required":["type","timestamp","data"]}}}},"responses":{"200":{"description":"Any 2xx within 15 seconds acknowledges it; anything else is retried after 1, 5, 25, 125, and 625 minutes."}}}},"webhook.test":{"post":{"summary":"A test event, sent from the settings page or the API.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The delivery's ID, the same on every retry and replay: skip one already handled.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"When this attempt was signed, in Unix seconds: refuse one more than 5 minutes off.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,` and the base64 HMAC-SHA256, under the endpoint secret's bytes (base64 after `whsec_`), of `<webhook-id>.<webhook-timestamp>.<body>`.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","const":"webhook.test"},"timestamp":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"data":{"type":"object","properties":{"orgId":{"type":"string"},"endpointId":{"type":"string"}},"required":["orgId","endpointId"]}},"required":["type","timestamp","data"]}}}},"responses":{"200":{"description":"Any 2xx within 15 seconds acknowledges it; anything else is retried after 1, 5, 25, 125, and 625 minutes."}}}}},"servers":[{"url":"https://app.farinu.com/api/v1"}]}