Skip to main content

API reference

All routes below are relative to /api/v1. /triggers/fire uses a non-strict object schema, so unrelated unknown body fields are stripped rather than refused; the personal-key authoring bodies are strict and reject 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 vendor endpoint authenticates with the yekar_session browser cookie; it does not accept a personal API key. A missing cookie 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/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/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/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.

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/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, 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. Treat turn.completed, session.changed, and stream.reset as signals to read authoritative session state again.

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

POST /api/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.

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/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.

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

GET /api/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": "gmail",
"name": "Gmail",
"description": "Read and manage Gmail messages.",
"tools": [
{
"name": "gmail.send-email",
"reference": "#gmail.send-email",
"description": "Send an email.",
"isReadonly": false,
"inputSchema": { "type": "object", "properties": {} }
}
]
}

POST /api/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/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 200,000 characters.
modelstringNoTrimmed and nonempty.
approvalToolsstring[]NoAt most 50 tool names, each 1–100 characters.

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

Publish the current draft using the key owner's edit_automation permission. The optional body is { "changeNote": string }, with a trimmed maximum of 2,000 characters. This plain API route has no approval-preview or approval gate.

On success it returns 201 Created. 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/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 #.

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

PUT /api/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-…"] }

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.

POST /api/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/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/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/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/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/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 input.prompt 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/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, connectionId, and the adapter-native eventType; optionally endpointSecret and one eventFilter comparison.

An event filter is { field, operator, value }. operator is exactly one of equals, not-equals, >, <, or contains; field must be actor data, occurredAt, or a payload path declared by the selected adapter catalog. Event-trigger creation validates the adapter, catalog event, organization rollout flag, 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.