{"openapi":"3.0.3","info":{"title":"Ajust Partner API","version":"1.0.0","description":"Machine-to-machine API for partner backends. Partners exchange registered client credentials for a scoped bearer token, then call partner-scoped resources. See https://api.ajust.example/ for the live reference UI.","contact":{"name":"Ajust Partner Engineering","email":"engineering@ajust.com.au"}},"servers":[{"url":"https://api.ajust.com.au","description":"Active environment"}],"tags":[{"name":"Discovery","description":"OAuth metadata partners read once during environment configuration."},{"name":"OAuth","description":"Client registration and token issuance for partner machine clients."},{"name":"Partners","description":"Partner-scoped resources keyed by the bearer token context."},{"name":"Identity Providers","description":"Upstream OIDC discovery metadata used during federated user handoff."},{"name":"Cases","description":"Partner-owned pre-handoff case intake state."},{"name":"Organisations","description":"Lookup of organisations in the Ajust directory, librarian-verified contact channels for an organisation, and partner attachment requests."},{"name":"Knowledge","description":"Read-only access to historical case patterns curated from prior casework. PII is redacted from document bodies; business contact details are preserved."}],"security":[],"paths":{"/.well-known/oauth-authorization-server":{"get":{"operationId":"oauthMetadata","tags":["Discovery"],"summary":"OAuth authorization server metadata","description":"Read Ajust OAuth metadata before wiring registration or token exchange into a partner backend. The response is environment-specific and safe to cache for short periods.","security":[],"responses":{"200":{"description":"Authorization server metadata.","content":{"application/json":{"schema":{"type":"object","properties":{"issuer":{"type":"string","format":"uri","description":"Canonical issuer URL for this environment."},"token_endpoint":{"type":"string","format":"uri","description":"Endpoint that issues partner access tokens."},"registration_endpoint":{"type":"string","format":"uri","description":"Internal bootstrap client registration endpoint."},"service_documentation":{"type":"string","format":"uri","description":"Reference UI for partner managers."},"response_types_supported":{"type":"array","items":{"type":"string"},"description":"Always empty; only the client_credentials grant is supported."},"grant_types_supported":{"type":"array","items":{"type":"string","enum":["client_credentials"]},"description":"Only client_credentials is supported."},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string","enum":["client_secret_basic","client_secret_post"]},"description":"Supported authentication methods at /oauth/token."},"scopes_supported":{"type":"array","items":{"type":"string","enum":["partner.registry:read","partner.identity_providers:write","partner.cases:create","partner.cases:read","partner.organisations:read","partner.organisations:write","partner.knowledge:read","partner.users:login"]},"description":"All partner scopes Ajust currently recognises."}},"required":["issuer","token_endpoint","registration_endpoint","service_documentation","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","scopes_supported"],"additionalProperties":false},"example":{"issuer":"https://api.ajust.example","token_endpoint":"https://api.ajust.example/oauth/token","registration_endpoint":"https://api.ajust.example/oauth/register","service_documentation":"https://api.ajust.example/","response_types_supported":[],"grant_types_supported":["client_credentials"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"scopes_supported":["partner.registry:read","partner.identity_providers:write","partner.cases:create","partner.cases:read","partner.organisations:read","partner.organisations:write","partner.knowledge:read"]}}}}}}},"/oauth/register":{"post":{"operationId":"registerClient","tags":["OAuth"],"summary":"Bootstrap register a machine client","description":"Internal bootstrap endpoint guarded by a shared registration token. Partner managers should use the self-service console at POST /manage/partners/{slug}/clients instead; this endpoint exists for protocol compatibility and controlled automation.","security":[{"registrationToken":[]}],"responses":{"201":{"description":"Client registered. The client_secret is shown once and must be stored immediately.","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string","description":"OAuth client_id partners use at /oauth/token."},"client_secret":{"type":"string","description":"OAuth client_secret. Shown exactly once. Store immediately; Ajust does not retain a copy that can be retrieved later."},"client_id_issued_at":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Unix timestamp (seconds) when the client was created."},"client_secret_expires_at":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Unix timestamp (seconds) when the secret expires. 0 means it does not expire."},"client_name":{"type":"string","description":"Echo of the requested client name."},"grant_types":{"type":"array","items":{"type":"string","enum":["client_credentials"]},"description":"Grant types this client may use."},"scope":{"type":"string","description":"Space-delimited scopes the client may request at the token endpoint."},"token_endpoint_auth_method":{"type":"string","enum":["client_secret_basic","client_secret_post"],"description":"How the client must authenticate at /oauth/token."},"ajust_partner_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side partner uuid the client belongs to."},"ajust_partner_slug":{"type":"string","description":"Stable slug of the partner shell the client belongs to."}},"required":["client_id","client_secret","client_id_issued_at","client_secret_expires_at","client_name","grant_types","scope","token_endpoint_auth_method","ajust_partner_id","ajust_partner_slug"],"additionalProperties":false},"example":{"client_id":"ajust_partner_5b9e...","client_secret":"ajust_partner_secret_8a3f...","client_id_issued_at":1716423723,"client_secret_expires_at":0,"client_name":"Partner A chatbot","grant_types":["client_credentials"],"scope":"partner.registry:read partner.cases:create","token_endpoint_auth_method":"client_secret_basic","ajust_partner_id":"b98a1d0c-1234-4f7a-9c0d-1d77ce4b1a90","ajust_partner_slug":"partner-a"}}}},"400":{"description":"`invalid_client_metadata` — Body failed validation, e.g. unsupported scope or slug.\n\n`invalid_request` — Body is not valid JSON.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_client_metadata"}}}},"401":{"description":"`invalid_request` — Registration bearer token missing or did not match.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_request"}}}}},"requestBody":{"description":"Dynamic client registration metadata (RFC 7591 subset).","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string","minLength":1,"maxLength":120,"description":"Human-readable client name shown to Ajust staff."},"scope":{"type":"string","description":"Space-delimited list of partner scopes. Only scopes already granted to the registered client may be requested.","example":"partner.registry:read partner.identity_providers:write partner.cases:create partner.cases:read partner.organisations:read partner.organisations:write partner.knowledge:read"},"grant_types":{"default":["client_credentials"],"description":"Only client_credentials is supported. Provided for RFC 7591 compatibility.","type":"array","items":{"type":"string","enum":["client_credentials"]}},"token_endpoint_auth_method":{"default":"client_secret_basic","description":"How the client authenticates at /oauth/token. Basic is recommended; post is supported for clients that cannot send Authorization headers.","type":"string","enum":["client_secret_basic","client_secret_post"]},"ajust_partner_slug":{"description":"Stable kebab-case slug Ajust uses to address this partner. Derived from client_name when omitted.","type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"required":["client_name","scope","grant_types","token_endpoint_auth_method"],"additionalProperties":false},"example":{"client_name":"Partner A chatbot","ajust_partner_slug":"partner-a","grant_types":["client_credentials"],"token_endpoint_auth_method":"client_secret_basic","scope":"partner.registry:read partner.cases:create"}}}}}},"/oauth/token":{"post":{"operationId":"mintToken","tags":["OAuth"],"summary":"Exchange client credentials for a partner access token","description":"Standard OAuth 2.0 client credentials grant. Authenticate with HTTP Basic (client_secret_basic) or by including client_id and client_secret in the body (client_secret_post). The returned bearer token is scoped to a subset of the scopes already granted to the client.","security":[{"clientCredentials":["partner.registry:read","partner.identity_providers:write","partner.cases:create","partner.cases:read","partner.organisations:read","partner.organisations:write","partner.knowledge:read","partner.users:login"]}],"responses":{"200":{"description":"Access token issued.","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string","description":"Opaque bearer token. Send as Authorization: Bearer <token>."},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer","exclusiveMinimum":true,"maximum":9007199254740991,"description":"Token lifetime in seconds. Currently 3600."},"scope":{"type":"string","description":"Space-delimited scopes the issued token carries."}},"required":["access_token","token_type","expires_in","scope"],"additionalProperties":false},"example":{"access_token":"ajust_pat_4f8a...","token_type":"Bearer","expires_in":3600,"scope":"partner.cases:create partner.cases:read"}}}},"400":{"description":"`invalid_request` — Request body is missing required fields.\n\n`invalid_scope` — Requested scope is not allowed for this client.\n\n`unsupported_grant_type` — grant_type is not client_credentials. Other grants are not supported.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_request"}}}},"401":{"description":"`invalid_client` — Client authentication failed.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_client"}}}}},"requestBody":{"description":"OAuth 2.0 token request form body.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"grant_type":{"type":"string","enum":["client_credentials"]},"scope":{"description":"Space-delimited subset of scopes already granted to the client.","type":"string"}},"required":["grant_type"],"additionalProperties":false},"example":{"grant_type":"client_credentials","scope":"partner.cases:create partner.cases:read"}}}}}},"/v1/partners/me":{"get":{"operationId":"partnerContext","tags":["Partners"],"summary":"Read the active partner token context","description":"Inspect which partner, client, and scopes Ajust resolved from the bearer token. Useful as an integration smoke test immediately after token exchange. Accepts any valid partner bearer; no specific scope is required because the response only echoes what the bearer already implies, and a fresh client needs a way to discover its granted scopes before calling scoped endpoints.","security":[{"partnerBearer":[]}],"responses":{"200":{"description":"Partner token context.","content":{"application/json":{"schema":{"type":"object","properties":{"partner":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side partner uuid."},"name":{"type":"string","description":"Partner display name."},"slug":{"type":"string","description":"Stable kebab-case partner slug."},"status":{"type":"string","enum":["draft","sandbox","active","suspended"],"description":"Lifecycle status. Only sandbox and active partners can call resource endpoints."}},"required":["id","name","slug","status"],"additionalProperties":false},"client_id":{"type":"string","description":"OAuth client_id Ajust resolved from the bearer token."},"token_scopes":{"type":"array","items":{"type":"string","enum":["partner.registry:read","partner.identity_providers:write","partner.cases:create","partner.cases:read","partner.organisations:read","partner.organisations:write","partner.knowledge:read","partner.users:login"]},"description":"Scopes the active token actually carries."}},"required":["partner","client_id","token_scopes"],"additionalProperties":false},"example":{"partner":{"id":"b98a1d0c-1234-4f7a-9c0d-1d77ce4b1a90","name":"Partner A","slug":"partner-a","status":"sandbox"},"client_id":"ajust_partner_5b9e...","token_scopes":["partner.registry:read"]}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}}}},"/v1/partners/me/identity-providers":{"post":{"operationId":"identityProvider","tags":["Identity Providers"],"summary":"Register an upstream OIDC identity provider","description":"Register the upstream OIDC provider Ajust will trust during federated user handoff. Ajust fetches the discovery URL and validates that the returned issuer matches the registered issuer. The client secret value itself is never stored; provide a reference (e.g. a Vault URI) Ajust can resolve at runtime.","security":[{"partnerBearer":["partner.identity_providers:write"]}],"responses":{"201":{"description":"Identity provider stored.","content":{"application/json":{"schema":{"type":"object","properties":{"identity_provider":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side identity provider id."},"provider_key":{"type":"string","description":"Provider key the partner registered."},"issuer":{"type":"string","format":"uri","description":"Discovered OIDC issuer URL."},"status":{"type":"string","enum":["sandbox","active","suspended"],"description":"Lifecycle status of the identity provider."}},"required":["id","provider_key","issuer","status"],"additionalProperties":false}},"required":["identity_provider"],"additionalProperties":false},"example":{"identity_provider":{"id":"5170e1a2-9c41-4f4d-9b8c-86a1c1e5c0f0","provider_key":"partner-a","issuer":"https://auth.partner.example","status":"sandbox"}}}}},"400":{"description":"`invalid_provider_metadata` — Discovery URL was unreachable, returned invalid metadata, used HTTP, or the discovered issuer did not match.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_provider_metadata"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}},"requestBody":{"description":"OIDC provider metadata.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider_key":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Stable kebab-case key the partner picks. Used as the namespace for federated subjects from this provider."},"issuer":{"type":"string","format":"uri","description":"OIDC issuer URL. Must match the issuer the discovery document returns."},"discovery_url":{"type":"string","format":"uri","description":"OIDC discovery document URL, typically issuer + /.well-known/openid-configuration."},"client_id_for_ajust":{"type":"string","minLength":1,"maxLength":255,"description":"OAuth client id the partner has provisioned for Ajust."},"client_secret_ref":{"description":"Opaque reference to where Ajust can fetch the client secret at runtime (e.g. a Vault URI). The secret value itself is never stored.","type":"string","minLength":1,"maxLength":255},"subject_type":{"default":"public","description":"OIDC subject type used by the upstream provider. Determines how Ajust hashes federated subjects.","type":"string","enum":["public","pairwise"]}},"required":["provider_key","issuer","discovery_url","client_id_for_ajust","subject_type"],"additionalProperties":false},"example":{"provider_key":"partner-a","issuer":"https://auth.partner.example","discovery_url":"https://auth.partner.example/.well-known/openid-configuration","client_id_for_ajust":"ajust-prod","client_secret_ref":"vault://partners/partner-a/ajust-client","subject_type":"public"}}}}}},"/v1/cases":{"post":{"operationId":"createCase","tags":["Cases"],"summary":"Create or update partner-owned case intake","description":"Create or update pre-handoff intake state for one partner conversation. The combination of partner and external_conversation_id is the partner-side idempotent key: a second POST with the same external_conversation_id updates the same row.","security":[{"partnerBearer":["partner.cases:create"]}],"responses":{"201":{"description":"Case intake created or updated.","content":{"application/json":{"schema":{"type":"object","properties":{"case":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side case id."},"status":{"type":"string","enum":["draft_intake","needs_more_information","shadow_case_created","plan_ready","handoff_required","claimed"],"description":"Where the case is in the intake lifecycle."},"external_conversation_id":{"type":"string","description":"Partner-side conversation id. The (partner, external_conversation_id) pair is the idempotent key."},"external_user_id":{"type":"string","description":"Partner-side user id (echo of the request)."}},"required":["id","status","external_conversation_id","external_user_id"],"additionalProperties":false},"next":{"type":"string","description":"Human-readable hint about the next step in the partner flow."}},"required":["case","next"],"additionalProperties":false},"example":{"case":{"id":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c","status":"draft_intake","external_conversation_id":"partner-thread-456","external_user_id":"partner-user-123"},"next":"Federated login or Ajust handoff must claim the case before live advocacy execution."}}}},"400":{"description":"`invalid_case` — Case intake body failed validation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_case"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}},"requestBody":{"description":"Case intake payload.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","minLength":1,"maxLength":255,"description":"Stable partner-side identifier for the end user."},"external_conversation_id":{"type":"string","minLength":1,"maxLength":255,"description":"Partner-side conversation or case id. Treated as the idempotent key per partner; re-posting with the same value updates the existing case."},"issue_summary":{"type":"string","minLength":1,"maxLength":4000,"description":"Free-text summary of the user issue. Used by the agent to start the playbook."},"business_name":{"description":"Name of the business the case is against, when the partner already knows it.","type":"string","minLength":1,"maxLength":255},"intake":{"default":{},"description":"Free-form key/value bag of partner-supplied intake state. Stored verbatim and made available to the agent.","type":"object","additionalProperties":{}},"return_url":{"description":"URL Ajust may use to deep-link the user back to the partner experience after a handoff.","type":"string","format":"uri"}},"required":["external_user_id","external_conversation_id","issue_summary","intake"],"additionalProperties":false},"example":{"external_user_id":"partner-user-123","external_conversation_id":"partner-thread-456","issue_summary":"Charged after cancellation.","business_name":"Example Telco","intake":{"amount_sought_cents":18000}}}}}}},"/v1/cases/{id}":{"get":{"operationId":"readCase","tags":["Cases"],"summary":"Read a partner-owned case","description":"Read the partner-owned intake record before or after the handoff boundary. A partner can read only case intake it created for its own partner record.","security":[{"partnerBearer":["partner.cases:read"]}],"responses":{"200":{"description":"Case record.","content":{"application/json":{"schema":{"type":"object","properties":{"case":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side case id."},"status":{"type":"string","enum":["draft_intake","needs_more_information","shadow_case_created","plan_ready","handoff_required","claimed"],"description":"Where the case is in the intake lifecycle."},"external_user_id":{"type":"string","description":"Partner-side user id (echo of the request)."},"external_conversation_id":{"type":"string","description":"Partner-side conversation id. The (partner, external_conversation_id) pair is the idempotent key."},"issue_summary":{"type":"string","description":"Free-text issue summary."},"business_name":{"nullable":true,"description":"Business the case is against, if known.","type":"string"},"intake":{"type":"object","additionalProperties":{},"description":"Free-form partner-supplied intake state, stored verbatim."},"return_url":{"nullable":true,"description":"Partner-supplied return URL, if any.","type":"string","format":"uri"},"thread_id":{"nullable":true,"description":"Ajust thread uuid once the case is claimed. Null while the case is still in pre-handoff state.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"created_at":{"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))$","description":"Case creation timestamp (ISO-8601)."},"updated_at":{"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))$","description":"Last update timestamp (ISO-8601)."}},"required":["id","status","external_user_id","external_conversation_id","issue_summary","business_name","intake","return_url","thread_id","created_at","updated_at"],"additionalProperties":false}},"required":["case"],"additionalProperties":false},"example":{"case":{"id":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c","status":"draft_intake","external_user_id":"partner-user-123","external_conversation_id":"partner-thread-456","issue_summary":"Charged after cancellation.","business_name":"Example Telco","intake":{"amount_sought_cents":18000},"return_url":null,"thread_id":null,"created_at":"2026-05-20T03:21:08.412Z","updated_at":"2026-05-20T03:21:08.412Z"}}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}},"404":{"description":"`not_found` — No case with this id is owned by the calling partner.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"not_found"}}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"Ajust case id returned by POST /v1/cases.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c"}]}},"/v1/cases/{id}/messages":{"post":{"operationId":"appendCaseMessages","tags":["Cases"],"summary":"Append partner conversation messages to a case","description":"Build the shadow transcript for a case before handoff. Each message is idempotent on (case, external_message_id): re-sending the same id updates the stored message instead of duplicating it, so the same history can be pushed incrementally or replayed as one batch immediately before requesting a handoff link. Send messages in conversation order — transcript order follows the order messages are received. When the user later claims the case inside Ajust, the transcript seeds the live thread so the Ajust agent continues with full context. Once the case is claimed this endpoint returns 409 and the live conversation continues inside Ajust.","security":[{"partnerBearer":["partner.cases:create"]}],"responses":{"201":{"description":"Messages stored on the shadow transcript.","content":{"application/json":{"schema":{"type":"object","properties":{"case_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side case id."},"accepted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Messages stored or updated from this request."},"transcript_length":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Total messages now stored on the case transcript."}},"required":["case_id","accepted","transcript_length"],"additionalProperties":false},"example":{"case_id":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c","accepted":3,"transcript_length":3}}}},"400":{"description":"`invalid_messages` — Message batch failed validation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_messages"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}},"404":{"description":"`not_found` — No case with this id is owned by the calling partner.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"not_found"}}}},"409":{"description":"`case_claimed` — The case has been claimed in Ajust; the transcript can no longer be amended.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"case_claimed"}}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"Ajust case id returned by POST /v1/cases.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c"}],"requestBody":{"description":"Batch of partner-side conversation messages.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"external_message_id":{"type":"string","minLength":1,"maxLength":255,"description":"Partner-side message id. Idempotent key per case; re-sending the same id updates the stored message instead of duplicating it."},"role":{"type":"string","enum":["user","assistant"],"description":"Who sent the message on the partner platform."},"text":{"type":"string","minLength":1,"maxLength":20000,"description":"Plain-text message content."},"sent_at":{"description":"When the message was sent on the partner platform (ISO-8601). Informational only; transcript order follows the order messages are received.","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|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["external_message_id","role","text"],"additionalProperties":false},"description":"Messages in conversation order. Send incrementally as the conversation progresses, or as one batch immediately before requesting a handoff link."}},"required":["messages"],"additionalProperties":false},"example":{"messages":[{"external_message_id":"partner-msg-1","role":"user","text":"I cancelled and still got charged $180.","sent_at":"2026-06-12T01:14:08.412Z"},{"external_message_id":"partner-msg-2","role":"assistant","text":"Which provider was this with?"},{"external_message_id":"partner-msg-3","role":"user","text":"Example Telco."}]}}}}}},"/v1/cases/{id}/handoff-link":{"post":{"operationId":"createHandoffLink","tags":["Cases"],"summary":"Mint a handoff link that moves the user into Ajust","description":"Create a single-use, short-expiry link the partner shows the user (e.g. behind a \"Continue with Ajust\" button). Opening the link sends the user to the Ajust web app, requires sign-in, then claims the case: Ajust creates a live thread owned by that user, seeded with the shadow transcript, and the Ajust agent continues the case from there. Mint the link at the moment the user acts rather than in advance — it expires after expires_in_seconds. Minting a link for an already-claimed case is allowed and acts as a re-link: a signed-in user who owns the claimed thread is taken straight back to it.","security":[{"partnerBearer":["partner.cases:create"]}],"responses":{"201":{"description":"Handoff link minted. The URL embeds a one-time secret; do not log it.","content":{"application/json":{"schema":{"type":"object","properties":{"handoff_url":{"type":"string","format":"uri","description":"Single-use URL to send the user to. It opens the Ajust web app, requires sign-in, then converts the case into a live Ajust thread seeded with the shadow transcript."},"expires_at":{"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))$","description":"When the link stops working."},"case_status":{"type":"string","enum":["draft_intake","needs_more_information","shadow_case_created","plan_ready","handoff_required","claimed"],"description":"Case status after minting the link."}},"required":["handoff_url","expires_at","case_status"],"additionalProperties":false},"example":{"handoff_url":"https://app.ajust.example/handoff/ajust_handoff_4f8a...","expires_at":"2026-06-12T01:36:08.412Z","case_status":"handoff_required"}}}},"400":{"description":"`invalid_request` — Link options failed validation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_request"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}},"404":{"description":"`not_found` — No case with this id is owned by the calling partner.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"not_found"}}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"Ajust case id returned by POST /v1/cases.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c"}],"requestBody":{"description":"Optional link options.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expires_in_seconds":{"default":900,"description":"Link lifetime in seconds. Defaults to 15 minutes. Mint the link when the user acts (e.g. clicks \"Continue with Ajust\") rather than in advance.","type":"integer","minimum":60,"maximum":86400}},"required":["expires_in_seconds"],"additionalProperties":false},"example":{"expires_in_seconds":900}}}}}},"/v1/cases/{id}/login-link":{"post":{"operationId":"createLoginLink","tags":["Cases"],"summary":"Mint a programmatic login link that signs the user into Ajust","description":"Create a single-use, short-expiry link that signs an end user straight into the Ajust app with no further sign-in step. The partner asserts the user email; Ajust find-or-creates the matching account, mints a session, then claims the case into a live thread seeded with the shadow transcript. This is the zero-integration alternative to /v1/cases/{id}/handoff-link, which requires the user to sign in themselves. Because the link establishes a session for the asserted email, this endpoint is gated by the privileged partner.users:login scope and is granted only to trusted partners. Mint the link at the moment the user acts rather than in advance — it expires after expires_in_seconds and works only once.","security":[{"partnerBearer":["partner.users:login"]}],"responses":{"201":{"description":"Login link minted. The URL embeds a one-time secret; do not log it.","content":{"application/json":{"schema":{"type":"object","properties":{"login_url":{"type":"string","format":"uri","description":"Single-use URL to send the user to. Opening it signs the user into Ajust as the asserted email (find-or-create), then converts the case into a live Ajust thread seeded with the shadow transcript. No further sign-in step is required."},"expires_at":{"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))$","description":"When the link stops working."},"case_status":{"type":"string","enum":["draft_intake","needs_more_information","shadow_case_created","plan_ready","handoff_required","claimed"],"description":"Case status after minting the link."}},"required":["login_url","expires_at","case_status"],"additionalProperties":false},"example":{"login_url":"https://app.ajust.example/api/partner-login?token=ajust_login_4f8a...","expires_at":"2026-06-30T01:36:08.412Z","case_status":"handoff_required"}}}},"400":{"description":"`invalid_request` — Email or link options failed validation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_request"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}},"404":{"description":"`not_found` — No case with this id is owned by the calling partner.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"not_found"}}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"Ajust case id returned by POST /v1/cases.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"7770b9c0-4a7c-4b3d-9a78-3aaf1c8a7c0c"}],"requestBody":{"description":"Asserted user email and optional link options.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":255,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","description":"The end user email Ajust should sign the user in as. The partner asserts ownership of this address; Ajust find-or-creates the matching account. Requires the partner.users:login scope."},"expires_in_seconds":{"default":900,"description":"Link lifetime in seconds. Defaults to 15 minutes. Mint the link when the user acts (e.g. clicks \"Continue with Ajust\") rather than in advance. The link is single-use.","type":"integer","minimum":60,"maximum":86400}},"required":["email","expires_in_seconds"],"additionalProperties":false},"example":{"email":"user@example.com","expires_in_seconds":900}}}}}},"/v1/organisations":{"get":{"operationId":"searchOrganisations","tags":["Organisations"],"summary":"Search Ajust organisations","description":"List organisations in the Ajust knowledge base. Use this before POSTing to /v1/partners/me/organisation-links to discover the organisation_id to attach to. Results are filtered by case-insensitive substring match on name.","security":[{"partnerBearer":["partner.organisations:read"]}],"responses":{"200":{"description":"Organisations matching the search.","content":{"application/json":{"schema":{"type":"object","properties":{"organisations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side organisation uuid."},"name":{"type":"string","description":"Organisation display name."},"industry":{"nullable":true,"description":"Industry tags Ajust has classified the organisation under.","type":"array","items":{"type":"string"}}},"required":["id","name","industry"],"additionalProperties":false}}},"required":["organisations"],"additionalProperties":false},"example":{"organisations":[{"id":"c9f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","name":"Example Telco","industry":["telecommunications"]}]}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}},"parameters":[{"name":"search","in":"query","required":false,"description":"Substring matched against organisation name (case-insensitive).","schema":{"type":"string","minLength":1,"maxLength":120},"example":"telco"},{"name":"limit","in":"query","required":false,"description":"Maximum rows to return. Defaults to 10, max 50.","schema":{"type":"integer","minimum":1,"maximum":50},"example":"20"}]}},"/v1/partners/me/organisation-links":{"get":{"operationId":"listOrganisationLinks","tags":["Organisations"],"summary":"List partner organisation attachment requests","description":"Return every organisation attachment request this partner has submitted, with current status (requested, approved, rejected, revoked). Attached organisations give Ajust richer context when handling case intake from this partner.","security":[{"partnerBearer":["partner.organisations:read"]}],"responses":{"200":{"description":"Partner organisation attachment requests.","content":{"application/json":{"schema":{"type":"object","properties":{"organisation_links":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side link id."},"organisation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side organisation uuid."},"name":{"type":"string","description":"Organisation display name."},"industry":{"nullable":true,"description":"Industry tags Ajust has classified the organisation under.","type":"array","items":{"type":"string"}}},"required":["id","name","industry"],"additionalProperties":false},"status":{"type":"string","enum":["requested","approved","rejected","revoked"],"description":"Current state of the attachment request. Auto-approved when the requesting partner manager email domain matches the organisation. Otherwise queued for admin review."},"reason":{"nullable":true,"description":"Partner-supplied justification, if any.","type":"string"},"decision_note":{"nullable":true,"description":"Admin-supplied note from the approve/reject decision.","type":"string"},"requested_at":{"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))$"},"decided_at":{"nullable":true,"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","organisation","status","reason","decision_note","requested_at","decided_at"],"additionalProperties":false}}},"required":["organisation_links"],"additionalProperties":false},"example":{"organisation_links":[{"id":"8a8d3e2c-1111-4a4a-9b0c-aaaaaaaaaaaa","organisation":{"id":"c9f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","name":"Example Telco","industry":["telecommunications"]},"status":"approved","reason":"Authorised BPO for Example Telco.","decision_note":"Auto-approved: requesting manager email domain matches organisation.","requested_at":"2026-05-23T03:21:08.412Z","decided_at":"2026-05-23T03:21:08.412Z"}]}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}}},"post":{"operationId":"createOrganisationLink","tags":["Organisations"],"summary":"Request attachment to an organisation","description":"Request that this partner be attached to an organisation in the Ajust knowledge base. Re-posting with the same organisation_id is idempotent and updates the existing request. Requests submitted via this machine endpoint always start as \"requested\" — auto-approval by email domain only fires when the request is submitted from a partner manager web session.","security":[{"partnerBearer":["partner.organisations:write"]}],"responses":{"201":{"description":"Attachment request recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"organisation_link":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side link id."},"organisation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side organisation uuid."},"name":{"type":"string","description":"Organisation display name."},"industry":{"nullable":true,"description":"Industry tags Ajust has classified the organisation under.","type":"array","items":{"type":"string"}}},"required":["id","name","industry"],"additionalProperties":false},"status":{"type":"string","enum":["requested","approved","rejected","revoked"],"description":"Current state of the attachment request. Auto-approved when the requesting partner manager email domain matches the organisation. Otherwise queued for admin review."},"reason":{"nullable":true,"description":"Partner-supplied justification, if any.","type":"string"},"decision_note":{"nullable":true,"description":"Admin-supplied note from the approve/reject decision.","type":"string"},"requested_at":{"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))$"},"decided_at":{"nullable":true,"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","organisation","status","reason","decision_note","requested_at","decided_at"],"additionalProperties":false}},"required":["organisation_link"],"additionalProperties":false},"example":{"organisation_link":{"id":"8a8d3e2c-1111-4a4a-9b0c-aaaaaaaaaaaa","organisation":{"id":"c9f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","name":"Example Telco","industry":["telecommunications"]},"status":"requested","reason":"Authorised BPO for Example Telco.","decision_note":null,"requested_at":"2026-05-23T03:21:08.412Z","decided_at":null}}}}},"400":{"description":"`invalid_link_request` — Body failed validation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_link_request"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}},"404":{"description":"`organisation_not_found` — No organisation exists with that id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"organisation_not_found"}}}}},"requestBody":{"description":"Organisation attachment request.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"organisation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Organisation id returned by GET /v1/organisations. Identifies the org the partner wants attached to."},"reason":{"description":"Optional partner-supplied justification (e.g. \"Authorised BPO for Example Telco\"). Visible to Ajust admins reviewing the request.","type":"string","minLength":1,"maxLength":1000}},"required":["organisation_id"],"additionalProperties":false},"example":{"organisation_id":"c9f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","reason":"Authorised BPO for Example Telco."}}}}}},"/v1/knowledge/search":{"get":{"operationId":"searchKnowledge","tags":["Knowledge"],"summary":"Search the Ajust knowledge base","description":"Run the same hybrid (full-text + semantic) lookup the Ajust agent uses during its Research phase. Returns historical patterns, outcomes, contact hints, and strategies from prior casework. Personal email addresses and Australian mobile numbers are redacted; business contact details are preserved.","security":[{"partnerBearer":["partner.knowledge:read"]}],"responses":{"200":{"description":"Knowledge documents matching the query.","content":{"application/json":{"schema":{"type":"object","properties":{"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Ajust-side knowledge document id. Stable across searches."},"content":{"type":"string","description":"Document body. PII (personal email addresses and Australian mobile numbers) is redacted; business contact details are preserved."},"source":{"type":"string","enum":["organisation","industry","generic"],"description":"Where the document was matched: organisation-specific, industry-specific, or generic."}},"required":["id","content","source"],"additionalProperties":false}}},"required":["documents"],"additionalProperties":false},"example":{"documents":[{"id":"d9f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","content":"[From Telstra cases]\nCustomer disputed double-billing after plan change. Telstra Complaints (complaints@telstra.com.au) responded within 48h and credited the disputed amount.","source":"organisation"},{"id":"e9f1a2b3-2222-4f7a-9c0d-1d77ce4b1a90","content":"[From Telecommunications cases]\nTIO escalation is effective when the provider has not responded within 10 business days.","source":"industry"}]}}}},"400":{"description":"`invalid_query` — Query parameters failed validation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_query"}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}},"parameters":[{"name":"query","in":"query","required":true,"description":"Concise search string. Do not include operators like OR or quotes.","schema":{"type":"string","minLength":1,"maxLength":200},"example":"telstra billing dispute"},{"name":"organisation","in":"query","required":false,"description":"Business name to scope results. When provided, response is split into organisation-specific and generic blocks (returned in one array, distinguished by the per-document source field).","schema":{"type":"string","minLength":1,"maxLength":120},"example":"Telstra"},{"name":"industry","in":"query","required":false,"description":"Industry tag. When combined with organisation, scopes only the generic block.","schema":{"type":"string","enum":["Telecommunications","Energy","Banking","Insurance","Retail","Subscription Services","Travel","Utilities","Other"]},"example":"Telecommunications"}]}},"/v1/organisations/{name}/channels":{"get":{"operationId":"getOrganisationChannels","tags":["Organisations"],"summary":"Get verified contact channels for an organisation","description":"Return the librarian-verified routing table for an organisation, ordered by priority. The status discriminator distinguishes a clean hit (ok) from look-up failures (not_found, ambiguous) and from organisations Ajust knows about but has no usable routing for (no_channels, all_dead). When status is ok, dead_channels exposes known-bad addresses for the same provider so callers can avoid retrying them.","security":[{"partnerBearer":["partner.organisations:read"]}],"responses":{"200":{"description":"Channel lookup result.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"organisation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"organisation_name":{"type":"string"},"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channel_type":{"type":"string","enum":["email","browser","contact_form","live_chat","phone"],"description":"Routing channel category."},"value":{"type":"string","description":"Channel address (email address, URL, phone number, or similar depending on channel_type)."},"label":{"nullable":true,"description":"Human-readable label, when curated.","type":"string"},"status":{"type":"string","enum":["active","dead","auth_required","in_person_only","phone_required","coaching_only"],"description":"Channel health. Active channels are safe to attempt; dead channels are blocklisted; the rest indicate channels that work only under specific conditions (auth, in-person, etc.)."},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Preference order; lower numbers should be attempted first."},"last_verified_at":{"nullable":true,"description":"When Ajust last verified this channel still worked.","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))$"},"notes":{"nullable":true,"description":"Free-form curator notes, when present.","type":"string"}},"required":["id","channel_type","value","label","status","priority","last_verified_at","notes"],"additionalProperties":false}},"dead_channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channel_type":{"type":"string","enum":["email","browser","contact_form","live_chat","phone"],"description":"Routing channel category."},"value":{"type":"string","description":"Channel address (email address, URL, phone number, or similar depending on channel_type)."},"label":{"nullable":true,"description":"Human-readable label, when curated.","type":"string"},"status":{"type":"string","enum":["active","dead","auth_required","in_person_only","phone_required","coaching_only"],"description":"Channel health. Active channels are safe to attempt; dead channels are blocklisted; the rest indicate channels that work only under specific conditions (auth, in-person, etc.)."},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Preference order; lower numbers should be attempted first."},"last_verified_at":{"nullable":true,"description":"When Ajust last verified this channel still worked.","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))$"},"notes":{"nullable":true,"description":"Free-form curator notes, when present.","type":"string"}},"required":["id","channel_type","value","label","status","priority","last_verified_at","notes"],"additionalProperties":false},"description":"Known-bad addresses for this organisation. Treat as a blocklist when cross-referencing other routing sources."}},"required":["status","organisation_id","organisation_name","channels","dead_channels"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["all_dead"]},"organisation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"organisation_name":{"type":"string"},"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channel_type":{"type":"string","enum":["email","browser","contact_form","live_chat","phone"],"description":"Routing channel category."},"value":{"type":"string","description":"Channel address (email address, URL, phone number, or similar depending on channel_type)."},"label":{"nullable":true,"description":"Human-readable label, when curated.","type":"string"},"status":{"type":"string","enum":["active","dead","auth_required","in_person_only","phone_required","coaching_only"],"description":"Channel health. Active channels are safe to attempt; dead channels are blocklisted; the rest indicate channels that work only under specific conditions (auth, in-person, etc.)."},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Preference order; lower numbers should be attempted first."},"last_verified_at":{"nullable":true,"description":"When Ajust last verified this channel still worked.","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))$"},"notes":{"nullable":true,"description":"Free-form curator notes, when present.","type":"string"}},"required":["id","channel_type","value","label","status","priority","last_verified_at","notes"],"additionalProperties":false}}},"required":["status","organisation_id","organisation_name","channels"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["no_channels"]},"organisation_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"organisation_name":{"type":"string"},"channels":{"type":"array","items":{"anyOf":[]},"minItems":0,"maxItems":0}},"required":["status","organisation_id","organisation_name","channels"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["not_found"]},"organisation_id":{"type":"string","nullable":true,"enum":[null]},"organisation_name":{"type":"string","nullable":true,"enum":[null]},"channels":{"type":"array","items":{"anyOf":[]},"minItems":0,"maxItems":0},"message":{"type":"string"}},"required":["status","organisation_id","organisation_name","channels","message"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["ambiguous"]},"organisation_id":{"type":"string","nullable":true,"enum":[null]},"organisation_name":{"type":"string","nullable":true,"enum":[null]},"channels":{"type":"array","items":{"anyOf":[]},"minItems":0,"maxItems":0},"candidates":{"type":"array","items":{"type":"string"}},"message":{"type":"string"}},"required":["status","organisation_id","organisation_name","channels","candidates","message"],"additionalProperties":false}]},"example":{"status":"ok","organisation_id":"c9f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","organisation_name":"Telstra","channels":[{"id":"f1f1a2b3-1111-4f7a-9c0d-1d77ce4b1a90","channel_type":"email","value":"complaints@telstra.com.au","label":"Complaints email","status":"active","priority":1,"last_verified_at":"2026-05-20T03:21:08.412Z","notes":"Typically responds within 48h."}],"dead_channels":[]}}}},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}}},"parameters":[{"name":"name","in":"path","required":true,"description":"Canonical business name. Case-insensitive exact match first, then fuzzy fallback (alphanumeric-normalised, so \"JB Hi-Fi\" and \"jbhifi\" resolve to the same organisation).","schema":{"type":"string","minLength":1,"maxLength":120},"example":"Telstra"}]}},"/v1/partners/me/organisation-links/{id}":{"delete":{"operationId":"withdrawOrganisationLink","tags":["Organisations"],"summary":"Withdraw an organisation attachment request","description":"Revoke an organisation attachment that this partner previously requested. Both pending and approved links can be withdrawn.","security":[{"partnerBearer":["partner.organisations:write"]}],"responses":{"204":{"description":"Link withdrawn."},"401":{"description":"`invalid_token` — Bearer token is missing, expired, revoked, or unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"invalid_token"}}}},"403":{"description":"`insufficient_scope` — Bearer token is valid but does not carry the scope required for this endpoint.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"insufficient_scope"}}}},"404":{"description":"`not_found` — No active link with that id belongs to the calling partner.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable error code."},"error_description":{"description":"Human-readable description. Not stable; for logs and developers.","type":"string"}},"required":["error"],"additionalProperties":false},"example":{"error":"not_found"}}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"Ajust-side link id (from GET /v1/partners/me/organisation-links).","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"8a8d3e2c-1111-4a4a-9b0c-aaaaaaaaaaaa"}]}}},"components":{"securitySchemes":{"partnerBearer":{"type":"http","scheme":"bearer","bearerFormat":"opaque","description":"Partner access token issued by POST /oauth/token. Send as Authorization: Bearer <token>."},"clientCredentials":{"type":"oauth2","description":"OAuth 2.0 client credentials grant. Use the registered partner client_id and client_secret.","flows":{"clientCredentials":{"tokenUrl":"https://api.ajust.com.au/oauth/token","scopes":{"partner.registry:read":"Read partner registry metadata, including the active token context.","partner.identity_providers:write":"Register and update upstream OIDC identity providers.","partner.cases:create":"Create or update partner-owned case intake.","partner.cases:read":"Read partner-owned case records.","partner.organisations:read":"Search the organisation directory, read verified contact channels for an organisation, and list partner attachment requests.","partner.organisations:write":"Request or withdraw partner attachment to an organisation.","partner.knowledge:read":"Search the Ajust knowledge base.","partner.users:login":"Mint a single-use programmatic login link that signs an end user directly into the Ajust app for an asserted email. Privileged: granted only to trusted partners and never included in the default scope set."}}}},"registrationToken":{"type":"http","scheme":"bearer","description":"Shared registration token used only by the internal bootstrap path POST /oauth/register."}}}}