Skip to main content

API reference

All routes below are relative to /api/public/v1, and the same routes are described in the generated OpenAPI document. The former /api/v1 addresses of every route on this page keep working unchanged. The one exception is POST /api/v1/auth/token, a browser-session route that stays where it is. Every request body that carries a credential - /triggers/fire, the session, message and turn-approval bodies, the personal-key agents/* bodies and the connection and credential bodies - is strict: an unknown field, including a misspelled callerCredentials, returns 400 VALIDATION rather than being stripped and the request running on a credential you did not choose. The personal-key authoring bodies are strict as well. Other bodies may still ignore unknown fields.

Short-lived user access tokens​

POST /api/v1/auth/token​

Mint a short-lived access token for the currently logged-in, organization-bound user. This endpoint authenticates with the browser session cookie; it does not accept a personal API key. A missing session or an account-only session that has not selected an organization returns 401 UNAUTHORIZED.

Returns 200 OK:

{
"token": "yku_eyJ…",
"expiresAt": "2026-08-20T18:30:00.000Z"
}

The yku_ token expires one hour after issuance. It represents the same user and organization as the browser session at issuance time. Authoring requests reread the user membership, so a disabled user is refused even while a previously issued token has time remaining.

Personal-key Agent conversations​

These routes authenticate with Authorization: Bearer yk_…. The key acts as its owner, and Agent and session lookups use that owner's organization and ordinary domain/session visibility. A missing or invalid key, an unknown id, or an id in another organization returns uniform 404 NOT_FOUND.

POST /api/public/v1/agents/:automationId/sessions​

Open a session on a published Agent and queue its first turn. The target must be an Agent (not a Flow), published, and executable by the key owner. Returns 202 Accepted.

FieldTypeRequiredNotes
messagestringYesTrimmed, 1–8,000 characters.
callerCredentialsobjectNoPer-integration credentials for this first turn. See Caller credentials.
{ "sessionId": "0b1f3c9a-…", "executionId": "64ed2e3d-…" }

POST /api/public/v1/agents/sessions/:sessionId/messages​

Append a message to an Agent session the key owner may participate in and queue the next turn. Visibility alone is insufficient: the key owner must be the session's creator/recorded owner or a domain Operator, Editor or Owner, with current domain membership. A visible session without participation permission returns 403 SESSION_NOT_DRIVABLE. The body has the same message and optional callerCredentials fields as the open-session route; newly supplied caller credentials belong only to this message's turn. Returns 200 OK.

{ "executionId": "64ed2e3d-…" }

GET /api/public/v1/agents/sessions/:sessionId​

Read the visible session's authoritative transcript and state. Personal chats require the creator; ordinary domain sessions require membership in the session's domain. Organization Owner/Admin status does not bypass either rule. Returns 200 OK with session details, including:

FieldMeaning
canParticipateWhether this caller can send new messages, after participation and lifecycle checks.
canManageWhether this caller can rename, archive, delete or set the session outcome.
readOnlyReasonA reason new messages are unavailable, or null.
messages[].authorWhen recorded, the authenticated author's userId, display name, and whether the author isYou for this caller. Historical and machine messages may have no author.
messagesTotalThe number of messages in the conversation. messages carries the latest 100; when messagesTotal is larger, older messages are not part of this read.
unconnectedIntegrationsPresent when the agent uses an integration its domain has not connected, so it cannot use that integration's tools yet. Each entry has the integrationId and a connectUrl - the Yekar.AI page where an owner of the agent's domain connects it.

These flags describe the current caller, not a permanent role on the conversation. Mutations check authorization again. Participation does not confer approval rights. See Roles and permissions.

GET /api/public/v1/agents/sessions/:sessionId/stream​

Open a visible session's Server-Sent Events stream with the personal key in the Authorization header. Reading the stream requires session visibility, not participation; it does not grant permission to send messages or decide approvals. A successful connection returns 200 OK with Content-Type: text/event-stream, starts with retry: 2000, sends a snapshot, and sends heartbeat comments every 25 seconds.

Each data: frame is a versioned JSON envelope containing sessionId, seq, ts, and event. Event types are snapshot, turn.started, message.delta, tool_call.started, tool_call.finished, caller_credential.failed, budget.failed, file.added, turn.suspended, nested.spawned, nested.waiting, turn.completed, session.changed, and stream.reset. A snapshot includes pendingApproval and pendingApprovalId; a live turn.suspended event includes approvalId and the gated tool name. nested.spawned announces an agent or flow the turn called, with its childSessionId or childRunId; nested.waiting means the turn is parked until that child settles. Treat nested.waiting, turn.completed, session.changed, and stream.reset as signals to read authoritative session state again.

curl -N "https://api.yekar.ai/api/public/v1/agents/sessions/$SESSION_ID/stream" \
-H "Authorization: Bearer $YEKAR_API_KEY" \
-H "Accept: text/event-stream"

POST /api/public/v1/agents/sessions/:sessionId/turn-approval​

Resolve a write-tool approval discovered through the session stream. The key owner must be able to view the session and must satisfy the existing approval route: the credential owner for a user-routed call, or an approver allowed by the domain's current approval policy otherwise. A confirmation can only be answered by its recorded initiating person; session participation alone does not authorize any of these decisions. Another organization's session returns uniform 404 NOT_FOUND; a visible session for which the key owner cannot approve returns 403 FORBIDDEN.

FieldTypeRequiredNotes
decisionstringYesapproved or rejected.
approvalIdUUIDNoThe id from turn.suspended or the stream snapshot. A stale or different id returns a conflict.
commentstringNoOptional decision comment, at most 2,000 characters.
callerCredentialsobjectNoFresh per-integration credentials for the parked execution. See Caller credentials.
editedArgsobjectNoAmended arguments for the pending tool call ("fix the recipient and send"). Honoured only with decision: "approved" on a turn parked for a tool call; a rejection carrying it, or a turn parked on anything other than a tool call, returns 400 VALIDATION. The call runs with these arguments, the tool-call record shows them, and the audit entry is marked as edited.
rememberForSessionbooleanNoApprove this tool for the rest of the conversation. Accepted only with decision: "approved" on a confirmation the agent's author asked for; an approval your organization requires is asked every time, and sending it there returns 400 VALIDATION.

Returns 200 OK with { "ok": true, "approvalId": "…", "decision": "approved" }. An approved decision queues the parked turn to resume; it does not synchronously wait for the resumed tool call to finish.

Personal-key Agent authoring​

These routes authenticate with either Authorization: Bearer yk_… (personal API key) or Authorization: Bearer yku_… (short-lived user access token) and use the authenticated user's current permissions. Cross-organization agent or flow ids return uniform 404 NOT_FOUND. A known agent or flow in the user's organization returns 403 FORBIDDEN when the user lacks the required domain permission. Request bodies are strict; unknown fields return 400 VALIDATION.

GET /api/public/v1/authoring/integrations?q=:term​

Search the integrations visible and available to the authenticated user's organization. The optional q value is trimmed, case-insensitive, and limited to 200 characters; it matches integration ids, names and descriptions as well as tool names and descriptions. The response contains at most 50 matching integrations. Each integration also carries eventTypes, the event types it can fire a trigger on (empty when it has none); the detail route below adds an events array with each event's type, name, description, schemaVersion and filterablePaths.

{
"integrations": [
{
"id": "yekar.gmail",
"name": "Gmail",
"description": "Read and manage Gmail messages.",
"tools": [
{
"name": "yekar.gmail.send-message",
"reference": "#yekar.gmail.send-message",
"description": "Send an email.",
"isReadonly": false
}
]
}
]
}

GET /api/public/v1/authoring/integrations/:id/connections​

List the domain connections in the user's organization that an event trigger on this integration could use, so an Agent trigger can be created with a known connectionId. Returns 200 OK with { "connections": [ { "id", "name" } ] }.

GET /api/public/v1/authoring/integrations/:id​

Return one integration visible and available to the authenticated user's organization. Each tool includes its wire name, ready-to-paste Procedure reference, description, isReadonly flag, and JSON inputSchema. The reference is always # followed by the full tool wire id. Integration-level references are not supported, so integrations themselves do not have a reference field. An unknown, unavailable, or other-organization integration id returns uniform 404 NOT_FOUND.

{
"id": "yekar.gmail",
"name": "Gmail",
"description": "Read and manage Gmail messages.",
"tools": [
{
"name": "yekar.gmail.send-message",
"reference": "#yekar.gmail.send-message",
"description": "Send an email.",
"isReadonly": false,
"inputSchema": { "type": "object", "properties": {} }
}
]
}

POST /api/public/v1/authoring/automations​

Create a draft Agent in a domain where the key owner has edit_automation. This route cannot create a Flow. Returns 201 Created; the response is the created agent with kind: "agent".

FieldTypeRequiredNotes
domainIdUUID stringYesDomain that will own the Agent.
namestringYesTrimmed, 1–120 characters.
descriptionstringNoTrimmed, at most 2,000 characters.
initialSetupobjectNoInitial draft Setup; accepts the same four fields as the setup patch below and is not published.
{
"id": "84cfee8e-…",
"domainId": "0e34d8cb-…",
"name": "Settlement assistant",
"description": null,
"kind": "agent"
}

PATCH /api/public/v1/authoring/automations/:id/setup​

Patch only the Agent's draft Setup. Omitted fields remain unchanged, and this route never creates a revision or moves the published pointer. Requires edit_automation; returns 200 OK with { "setup": … }.

FieldTypeRequiredNotes
principlesstringNoAt most 100,000 characters.
procedurestringNoAt most 100,000 characters.
modelstringNoTrimmed and nonempty.
confirmToolsstring[]NoAt most 50 tool names, each 1–100 characters.

The body is validated strictly: an unknown key is refused with 400 VALIDATION rather than ignored. approvalTools was the former name of confirmTools and is fully retired, so a client still sending it fails rather than silently not gating.

POST /api/public/v1/authoring/automations/:id/publish​

Publish the current draft using the key owner's edit_automation permission. The optional body is { "changeNote": string, "procedureMode": "free_text" | "structured" }. changeNote has a trimmed maximum of 2,000 characters. procedureMode chooses how the published Procedure runs - Free text (free_text) or Structured (structured), the same choice as the Publish dialog; leave it out to keep the mode the draft already has. This plain API route has no approval-preview or approval gate.

On success it returns 200 OK. belt is the server-derived plannedAgentBindings result that was granted by the publish; each entry contains integrationId and identityMode.

{
"revision": 1,
"belt": [{ "integrationId": "yekar.http", "identityMode": "service" }]
}

If the mechanical procedure verifier finds problems, the route returns 422 VALIDATION; error.details.findings is the findings array so the caller can revise the draft.

{
"error": {
"code": "VALIDATION",
"message": "This procedure isn't ready to publish",
"details": { "findings": [{ "message": "…", "span": "…" }] }
}
}

GET /api/public/v1/authoring/automations/:id/verify​

Run the same mechanical verifier used by publish against the Agent's current draft without publishing or changing any state. Requires edit_automation. Cross-organization ids return uniform 404 NOT_FOUND.

Returns 200 OK. findings has the same shape used by publish's 422 response, and plannedBelt is the server-derived plannedAgentBindings result for the current draft. warnings reports advisory authoring mistakes and never blocks publishing. empty_belt means a non-empty Procedure derives no integrations; unreferenced_tool_id identifies a visible tool wire id written without its required leading #; not_connected names an integration the agent would use that is not ready for you in its domain, with integrationId, a missing reason (connection, sign_in, door_shut, settings or not_installed) and a detail sentence that says what to do.

{
"findings": [{ "kind": "unresolved_ref", "span": "#unknown.send", "detail": "…", "proposal": null }],
"plannedBelt": [{ "integrationId": "yekar.http", "identityMode": "service" }],
"warnings": []
}

PUT /api/public/v1/authoring/automations/:id/knowledge-selection​

Set an Agent's organization-knowledge selection using the key owner's manage_knowledge permission. Returns 200 OK with the normalized mode and deduplicated moduleIds.

FieldTypeRequiredNotes
modestringYesOne of none, org, domain, or modules.
moduleIdsUUID[]NoUp to 100 module ids; omission is equivalent to an empty selection.
{ "mode": "modules", "moduleIds": ["61753187-…"] }

Agent triggers​

These four routes manage the triggers of one Agent with the key owner's manage_triggers permission in the Agent's domain. A trigger created here is subscribed to that Agent and starts switched off; enable it from the product once it is ready. The body of POST is the same discriminated union as POST /api/v1/triggers below, without domainId - the Agent's domain is used - and an event trigger may omit connectionId until it is enabled. PATCH takes the fields of the trigger's own type (name, cron, timezone, input for a schedule; name for an API trigger; name, connectionId, eventType, eventFilter for an event trigger) and refuses an empty body or a field belonging to another type with 400 VALIDATION. PATCH and DELETE work only while the trigger is switched off and subscribed to this Agent alone; a shared or enabled trigger returns 400 VALIDATION. A trigger that belongs to another Agent returns uniform 404 NOT_FOUND.

RouteReturns
GET /api/public/v1/authoring/automations/:id/triggers200 OK with { "triggers": [ … ] } - the triggers this Agent is subscribed to.
POST /api/public/v1/authoring/automations/:id/triggers201 Created with { "trigger": … }.
PATCH /api/public/v1/authoring/automations/:id/triggers/:triggerId200 OK with { "trigger": … }.
DELETE /api/public/v1/authoring/automations/:id/triggers/:triggerId204 No Content.

Each trigger carries id, name, type, status, cron, timezone, input, eventFilter, and - for an event trigger - event with its source integration, connection and event type.

Agent tests​

These five routes write and run simulated tests of one Agent - the tests the Agent Builder proposes - using the key owner's edit access to the Agent. A simulated test is a conversation plus the result each tool returns, so nothing it tests runs for real: every tool call is answered from the script, and a call the script does not cover is refused without running. Tests require Evals to be available to the workspace.

RouteReturns
POST /api/public/v1/authoring/automations/:id/eval-cases201 Created with { "id" }.
GET /api/public/v1/authoring/automations/:id/eval-cases200 OK with { "cases": [ … ] }. A held-out case lists only its id, name and holdout.
DELETE /api/public/v1/authoring/automations/:id/eval-cases/:caseId200 OK. Only a case this API created can be removed; a person's case returns 400.
POST /api/public/v1/authoring/automations/:id/eval-suites202 Accepted with { "suiteRunId", "runs" }. Tests the Agent's saved draft.
GET /api/public/v1/authoring/automations/:id/eval-suites/:suiteId200 OK with each case's result. ?waitSeconds= (0-45) waits for the run to finish.

POST …/eval-cases takes name, turns (1-10 messages, in order), script (up to 50 { "tool", "response", "failed" } entries, used in order for each tool), holdout and assertions (1-20 checks, each { "kind", "value", "label" }). Every check is validated before the case is created, so a refused check leaves nothing behind. token_budget cannot be used on a simulated case because there is no recording to measure against; tool_called_with (tool|/json/pointer|expected) and tool_call_count (tool|n) check what a call sent and how often.

POST …/eval-suites takes repeat (1-5, default 1): each case runs that many times, and a case passes only when every run passes. Each result reports standing - pass, fail, error, unscored or running - with runs, passed and failed, plus failedChecks and the failing conversation for a case that is not held out. A held-out case reports its standing and counts only, so a fix cannot be written against it. Runs spend model tokens like any conversation.

Personal-key knowledge authoring​

These routes authenticate with either Authorization: Bearer yk_… (personal API key) or Authorization: Bearer yku_… (short-lived user access token) and use the authenticated user's current permissions. A base or module id in another organization returns uniform 404 NOT_FOUND; a resource in the user's organization returns 403 FORBIDDEN when the user lacks manage_knowledge in its domain. Request bodies use the same schemas as the corresponding console routes.

GET /api/public/v1/authoring/knowledge-modules?domainId=:domainId​

List the domain-owned knowledge modules of one domain, so a selection can name them. domainId is required. Returns 200 OK with { "modules": [ { "id", "name", "description", "kind" } ] }.

POST /api/public/v1/authoring/knowledge-modules​

Create a domain- or organization-owned knowledge module. Domain modules require manage_knowledge in the selected domain; organization modules require organization-admin authority. Returns 201 Created with the created module.

FieldTypeRequiredNotes
kindstringYesdomain or org.
domainIdUUID stringDomain onlyRequired for domain; forbidden for org.
namestringYesTrimmed, 1–200 characters.
descriptionstringNoAt most 10,000 characters.

POST /api/public/v1/authoring/knowledge-bases/:baseId/articles​

Create a manual draft article in a knowledge base where the key owner has manage_knowledge. The article is not published by this route. Returns 201 Created with the created article.

FieldTypeRequiredNotes
titlestringYesTrimmed, 1–240 characters.
draftBodystring/nullNoDraft text, at most 2,000,000 characters.

POST /api/public/v1/authoring/knowledge-modules/:moduleId/articles​

Add an existing published article to a module where the key owner has manage_knowledge. Both resources must belong to the key owner's organization and satisfy the module's domain/organization membership rules. Returns 201 Created with the membership row. An unpublished article or incompatible membership returns 409 CONFLICT.

FieldTypeRequiredNotes
articleIdUUID stringYesPublished article to add.

MCP endpoint​

POST /api/public/v1/mcp​

A JSON-RPC endpoint (Streamable HTTP) that presents your published agents as MCP tools to a client such as Claude Code, Cursor or VS Code. It authenticates with the same Authorization: Bearer yk_… personal key and serves initialize, ping, tools/list and tools/call. Because its methods live in the request body rather than in the path, it is documented on its own page: MCP server.

Retry fire requests safely​

POST /api/public/v1/triggers/fire accepts an optional idempotency key, in an Idempotency-Key header or a body idempotencyKey field. If both are present, the header wins. Use 1–128 characters containing only letters, numbers, dot, underscore, colon, or hyphen. Repeated headers and comma-separated values are rejected.

Reuse the same key only for a retry of the same request. A completed replay returns the original HTTP status and body with Idempotency-Replayed: true. Without a key, every accepted retry starts another fire operation. Retrying after a timeout without a key can produce duplicate effects.

The reservation window is 24 hours. Once it expires, a new request can reuse the key immediately.

You can also put the key in the request body:

{
"triggerId": "e8a9088e-…",
"input": { "month": "2026-07" },
"idempotencyKey": "reconcile-2026-07"
}

The key is scoped to the key owner's organization. Request equality covers the target, input, and callerCredentials; the key itself is excluded. Credential material, including credential values, participates in this comparison, but only a keyed digest is retained for idempotency - the material is not stored in the idempotency record.

A refreshed end-user token is therefore a different request. For example, if you lose the original 202 response and the end user's token is refreshed before you retry, reusing the old key returns 409 IDEMPOTENCY_KEY_REUSED; it does not replay work authorized with the earlier token. Mint a new idempotency key whenever credential material changes, because the changed authority makes it a new logical operation.

A replay returns 202 Accepted, the original runId or sessionId, and Idempotency-Replayed: true; it never starts another execution. Reusing the key with a different request returns 409 IDEMPOTENCY_KEY_REUSED. If the original request is still being recorded, the retry returns 409 IDEMPOTENCY_IN_PROGRESS with Retry-After: 2; retry the identical body after that interval.

POST /api/public/v1/triggers/fire​

Fire an active API trigger. One request fans out to every subscribed agent and flow, starting one session for each successful start. Authenticate with a personal API key. Returns 202 Accepted.

FieldTypeRequiredNotes
triggerIdUUID stringYesMust identify an active API trigger in a domain the key owner can execute in.
inputobjectNoDelivered verbatim to each Flow. An Agent receives the first non-empty string among input.prompt, input.message, input.text and input.task when present, otherwise the payload rendered as natural-language text. Defaults to {}.
callerCredentialsobjectNoPer-integration credentials, narrowed per subscriber. See Caller credentials.
callerCredentialExpiresAtstringNoISO-8601 expiry for the supplied material.
idempotencyKeystringNoRetry key for this logical request. See Retry fire requests safely.

A subscriber that binds an integration at caller identity receives only the credentials for the integrations it actually binds. A subscriber that requires caller material the fire did not supply is refused with CALLER_CREDENTIALS_REQUIRED and is reported in the trigger's Run history; the other subscribers still start. Supplied integration ids that no subscriber uses are recorded as diagnostic metadata, never as credential material.

{
"sessions": [{ "automationId": "84cfee8e-…", "sessionId": "0b1f3c9a-…" }]
}

One entry is returned for each agent or flow that started. sessions: [] means the trigger had no subscribers or every start failed; the trigger Run history distinguishes the skipped and failed outcomes and retains per-subscriber results.

curl -X POST "https://api.yekar.ai/api/public/v1/triggers/fire" \
-H "Authorization: Bearer $YEKAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "triggerId": "e8a9088e-…", "input": { "month": "2026-07" } }'
const { sessions } = await yekar.triggers.fire({
triggerId: "e8a9088e-…",
input: { month: "2026-07" },
});

POST /api/v1/triggers​

Create a domain trigger with the signed-in tenant session. Returns 201 Created with { "trigger": … }. The request is a discriminated union on type:

  • schedule: domainId, name, cron, and optional timezone and object input.
  • api: domainId and name.
  • event: domainId, name, sourceIntegrationId, the adapter-native eventType, and connectionId - required unless the source is hosted inbound email (yekar.email), which needs no connection; optionally endpointSecret (16–500 characters), registerWithProvider (register the subscription with the provider on creation) and one eventFilter comparison.

An event filter is { field, operator, value }. operator is exactly one of equals, not-equals, >, <, contains, or in; field must be actor data, occurredAt, or a payload path declared by the selected adapter catalog. Event triggers are enabled per workspace, on request - ask Yekar.AI to switch them on; creating one while they are off returns 400 VALIDATION. Event-trigger creation validates the adapter, catalog event, provider-app prerequisites, connection ownership, and filter before committing the trigger and its subscription atomically.

{
"type": "event",
"domainId": "0e34d8cb-…",
"name": "Telegram messages",
"sourceIntegrationId": "yekar.telegram",
"connectionId": "c228f835-…",
"eventType": "message",
"eventFilter": { "field": "actor.username", "operator": "equals", "value": "sahil" }
}

Fire credential and idempotency options​

POST /triggers/fire accepts callerCredentials, optional callerCredentialExpiresAt (ISO-8601), and idempotency through either the Idempotency-Key header or body idempotencyKey. If both keys are present, the header wins. Credentials are narrowed per subscriber; unused supplied integration ids are diagnostic metadata only.