Customer tenant API
The endpoints in Partner API reference manage your fleet: provisioning customers, minting leases, vouching workers, setting entitlements. This page covers the other half - driving what is inside a customer organization, so the people using your product never sign in to Yekar.AI.
Everything lives under /api/partner/v1/organizations/{orgId} and mirrors the product surface: agents and flows, connections and credentials, domains and members, triggers and schedules, sessions and runs, approvals, knowledge, apps, webhooks, events, evals, and organization settings.
How a request is authorized
Three things identify every call, and each is checked against the one before it:
POST /api/partner/v1/organizations/{orgId}/automations/{id}/publish
Authorization: Bearer ykp_00000000-0000-4000-8000-000000000001.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
X-Yekar-Subject: your-user-id-42
| Part | What it proves |
|---|---|
Authorization: Bearer ykp_… | Which platform is calling. |
{orgId} in the path | Which customer - it must be an active follower of that platform. |
X-Yekar-Subject | Which provisioned person is acting. |
The credential above is an obviously fake, structurally valid example.
Because the organization is addressed in the path rather than baked into a token, one long-lived key reaches your whole fleet. A nightly sweep across 500 customers is 500 calls on one key, not 500 leases.
Your key is an attestation, not an authority
This is the part worth internalizing, because it decides how you design your integration.
A ykp_ key proves who is asking. It confers no permission of its own. What a call may do is decided entirely by the org and domain roles of the person named in X-Yekar-Subject - evaluated by the same permission system that governs someone clicking in the Yekar.AI UI.
So the model is: when your platform needs to do something that requires a permission, provision a user who holds that permission, and act as them.
You provision that person with POST /organizations/{orgId}/workers:
{
"partnerSubject": "your-user-id-42",
"orgRole": "admin",
"domains": [{ "domainId": "…", "domainRole": "editor" }]
}
domains is declarative when you send it: after the call that person holds exactly the roles listed, and any role you omit is removed. Omit the field entirely and their existing roles are left alone. That is what lets you narrow access as well as widen it.
orgRole is admin or member. Organization administration - settings, AI provider keys and inviting Members - requires admin. Creating domains may also be allowed for Members by organization policy. The organization owner's role cannot be changed by a vouch; that request is refused rather than silently ignored.
Which role for which job
Roles are the same everywhere, so you can reason about them once rather than per customer.
| To do this | The subject needs |
|---|---|
| Read domain agents, flows, ordinary sessions and knowledge | Any role in that domain (viewer or above) |
| Start a published agent/flow or participate in their own session | Any role in that domain |
| Participate in a teammate's domain session; manage triggers and webhooks | operator, editor or owner in that domain |
| Decide a domain-routed approval | The domain's approval policy and any Flow gate role floor; defaults to operator or above |
| Author agents, flows and knowledge; start evaluations or draft tests | editor or owner in that domain |
| Decide OCR spending for scanned documents | Organization admin |
| Manage domain shared connections and credentials | Domain owner |
| Manage domain members and assign any domain role | Domain owner, or organization admin across its domains |
| Organization settings, AI keys, invitations for Members | Organization admin |
| Create domains | Organization admin, or member when organization policy allows it |
An organization Admin cannot read someone else's personal chat or bypass domain permissions. Linked Builder conversations require current edit permission on their subject agent. See Roles and permissions for the complete matrices, ownership rules and approval exceptions.
A subject provisioned in no domain can authenticate and will be refused everything domain-scoped - which is a useful default for a service identity that should only touch one area.
What this is not
Roles bound the blast radius of a call and make the audit trail name a real person. They are not a boundary against you: your platform may vouch anyone into any role in its own followers. What bounds you is the organization check - a key reaches only organizations its own platform provisioned.
Refusals
Every break in the chain answers the same 404: an unknown key, another platform's organization, a missing or unknown X-Yekar-Subject, a revoked mapping, a deactivated membership, a detached follower. They are deliberately indistinguishable, so a key cannot be used to discover which organizations exist or which of another platform's people are active.
Once the chain resolves, refusals are the ordinary product ones - 403 when the acting person lacks the permission, 404 for a record they cannot see.
Starting work
Three endpoints start an execution and accept an end user's own credential for the integrations it runs on:
POST /organizations/{orgId}/sessions- open a conversationPOST /organizations/{orgId}/sessions/{sessionId}/messages- continue onePOST /organizations/{orgId}/automations/{id}/sessions- fire one runPOST /organizations/{orgId}/automations/{id}/test-runs- test the current draft of a flow
They take callerCredentials exactly as the caller credentials contract describes, and reconcile it against what the target binds at caller identity. A credential for an integration the target does not bind at caller identity is refused by name rather than accepted and ignored.
A credential is scoped to the single execution the request seeds and is never stored as a reusable connection. On POST /sessions, it must accompany a message: a session opened with nothing said has no execution to authorize, and sending one anyway is refused with CALLER_TOKENS_NO_ANCHOR.
POST /automations/{id}/bulk-retry and POST /automations/{id}/eval-replays refuse callerCredentials by name. Both run on stored identity by construction, so a supplied token could only ever be silently ignored.
Connecting OAuth integrations
Connecting Google, Microsoft, Slack and the rest still needs the end user's browser to reach the vendor's consent screen. A connect link opens that screen without a Yekar.AI session and brings the person back to your product:
POST /organizations/{orgId}/connect-linkswith{ "integrationId": "yekar.gmail", "returnUrl": "https://app.example.com/connected" }returns201 { url, expiresAt }. Openurlin the user's browser. It works once, lasts 10 minutes, and stores the resulting sign-in as the acting subject's own personal connection - so theX-Yekar-Subjecton this call is the person who will be connected.returnUrlmust sit under one of your registered return URLs. Register them once per platform withPUT /api/partner/v1/platform/oauth-return-urls { "returnUrls": [...] }- HTTPS origins plus path prefixes, up to 50;GETon the same path reads them back. A URL outside that list is refused with400 OAUTH_RETURN_URL_NOT_ALLOWED.- The link signs in through Yekar.AI's managed app for that integration, or through your own client registered with
PUT /oauth-appsand selected withPUT /oauth-apps/{appKey}/state { "state": "byok" }so the consent screen carries your name. When the organization has neither, the call is refused with400 MANAGED_OAUTH_UNAVAILABLE. - After the consent screen the browser lands on
returnUrlwithstatus=connectedandconnectionId, or withstatus=errorand anerrorofoauth_access_denied,oauth_provider_error,oauth_code_missingoroauth_exchange_failed. Nothing else about the exchange travels on that redirect.
Key-based integrations need no browser at all: post the credential to POST /organizations/{orgId}/domains/{id}/connections.
Everything else in the product is reachable, including document upload (single and streamed multi-file batches with per-document outcomes), OCR preflight and decisions, and session file transfer. Uploaded documents follow the same journey as in the product: first successful extraction publishes automatically as the acting subject, and a scanned document waits on an OCR decision - an org-admin subject's call to …/ocr-decision - unless the organization's standing OCR authorization is set.
Worked example: onboarding a customer end to end
1. POST /organizations → customer org
2. POST /organizations/{orgId}/workers → an admin subject
3. POST /organizations/{orgId}/domains → shape the org
4. POST /organizations/{orgId}/users → invite their people
(whitelabel: returns an activation link, sends no Yekar.AI-branded mail)
5. POST /organizations/{orgId}/domains/{id}/connections → their credentials
6. POST /organizations/{orgId}/domains/{id}/automations → author an agent
7. PATCH .../automations/{id}/setup → POST .../setup/publish
8. POST /organizations/{orgId}/automations/{id}/webhooks → completion callbacks
9. POST /organizations/{orgId}/sessions → talk to it
Steps 3–7 need a subject with the roles that step requires. Steps 5 and 6 are the ones people miss: connections need owner on the domain, authoring needs editor.