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.
| Field | Type | Required | Notes |
|---|---|---|---|
message | string | Yes | Trimmed, 1–8,000 characters. |
callerCredentials | object | No | Per-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:
| Field | Meaning |
|---|---|
canParticipate | Whether this caller can send new messages, after participation and lifecycle checks. |
canManage | Whether this caller can rename, archive, delete or set the session outcome. |
readOnlyReason | A reason new messages are unavailable, or null. |
messages[].author | When 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.
| Field | Type | Required | Notes |
|---|---|---|---|
decision | string | Yes | approved or rejected. |
approvalId | UUID | No | The id from turn.suspended or the stream snapshot. A stale or different id returns a conflict. |
comment | string | No | Optional decision comment, at most 2,000 characters. |
callerCredentials | object | No | Fresh 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".
| Field | Type | Required | Notes |
|---|---|---|---|
domainId | UUID string | Yes | Domain that will own the Agent. |
name | string | Yes | Trimmed, 1–120 characters. |
description | string | No | Trimmed, at most 2,000 characters. |
initialSetup | object | No | Initial 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": … }.
| Field | Type | Required | Notes |
|---|---|---|---|
principles | string | No | At most 100,000 characters. |
procedure | string | No | At most 200,000 characters. |
model | string | No | Trimmed and nonempty. |
approvalTools | string[] | No | At 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.
| Field | Type | Required | Notes |
|---|---|---|---|
mode | string | Yes | One of none, org, domain, or modules. |
moduleIds | UUID[] | No | Up 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.
| Field | Type | Required | Notes |
|---|---|---|---|
kind | string | Yes | domain or org. |
domainId | UUID string | Domain only | Required for domain; forbidden for org. |
name | string | Yes | Trimmed, 1–200 characters. |
description | string | No | At 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.
| Field | Type | Required | Notes |
|---|---|---|---|
title | string | Yes | Trimmed, 1–240 characters. |
draftBody | string/null | No | Draft 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.
| Field | Type | Required | Notes |
|---|---|---|---|
articleId | UUID string | Yes | Published 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.
| Field | Type | Required | Notes |
|---|---|---|---|
triggerId | UUID string | Yes | Must identify an active API trigger in a domain the key owner can execute in. |
input | object | No | Delivered verbatim to each Flow. An Agent receives input.prompt when present, otherwise the payload rendered as natural-language text. Defaults to {}. |
callerCredentials | object | No | Per-integration credentials, narrowed per subscriber. See Caller credentials. |
callerCredentialExpiresAt | string | No | ISO-8601 expiry for the supplied material. |
idempotencyKey | string | No | Retry 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 optionaltimezoneand objectinput.api:domainIdandname.event:domainId,name,sourceIntegrationId,connectionId, and the adapter-nativeeventType; optionallyendpointSecretand oneeventFiltercomparison.
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.