API REFERENCE
The API
One base URL, one Bearer key, and a handful of endpoints. Everything the widget does, you can do server to server.
Base URL
Every endpoint lives under a single host. Paths are versioned with a /v1 prefix.
https://agentifys.ai
Authentication
Authenticate every request with your project key as a Bearer token. Keys start with era_ and belong to one project. Create and rotate them in the admin under Settings.
Authorization: Bearer era_your_project_key
VITE_ / NEXT_PUBLIC_ variable or any bundle you ship.A missing or wrong key returns PROJECT_INVALID_KEY with a 401. Once the key resolves, most endpoints run the origin check described below.
Allowed origins
A project carries a list of allowed origins, empty by default. While it is empty the check is skipped and any caller holding a valid key is served. The moment the list has one entry, every request to a checked endpoint must present a matching Origin header or it is refused with ORIGIN_NOT_ALLOWED and a 403.
The comparison is an exact string match on scheme + host + port, after any trailing slash is stripped from the incoming header. There are no wildcards and no subdomain matching. https://example.com does not admit https://www.example.com, and https://app.example.com does not admit https://app.example.com:8443. Add every origin you actually serve from.
https://app.example.com https://www.example.com http://localhost:5173
Origin header at all, the absent header is read as the empty string, and the empty string never matches a non-empty list. A backend calling a project that has any allowed origin configured is refused exactly like an unlisted website.The check runs on /v1/chat, /v1/history, /v1/feedback, DELETE /v1/sessions/{id}, and every /v1/knowledge, /v1/scheduled-tasks, and /v1/oauth/mcp route. Two endpoints skip it: the vault (/v1/credentials, all three methods), which is server-to-server by design, and GET /v1/project-config, which the widget loads before anything else.
This is an application-level check, not a CORS restriction. CORS headers are permissive, so the browser will hand you the response rather than blocking it, and what you read is a 403 JSON body.
Two integration shapes
How you integrate decides what the allowlist should contain. Pick one per project.
Browser-direct. The embedded widget, or your own front-end code calling agentifys.ai from the page. Every request carries the page's origin. Fill the allowlist in: it is the only thing standing between your key and anyone who copies it out of your HTML.
Server-proxy. Your backend holds the key and calls Agentifys; the browser only ever talks to your server. Those requests arrive with no origin, so the allowed-origins list must be left empty. One entry is enough to break every call your backend makes. Access control in this shape is your own perimeter — the session or token your server already checks before it proxies.
Origin header from your proxy does make the call pass, but the header is then a value your own server chose, so the allowlist stops being a control and is only ceremony.User identity
Most endpoints take a user_context object that names the end user. This is what powers per-user memory, private knowledge, the vault, and scheduling.
A project runs in one of two identity modes. In open mode the identity is trusted as sent, good for personalization. In signed mode the identity is tamper-proof: you sign it server side with an HMAC secret, and that gates private uploads, the vault, per-user MCP OAuth, and the My-tasks panel.
| Field | Type | Description |
|---|---|---|
id | string | Stable unique id for the end user in your system. Required in signed mode, where a verified context without one is rejected with IDENTITY_ID_REQUIRED. In open mode the whole object may be empty, and the user is treated as anonymous. |
name | string | Display name. |
email | string | Email address. |
role | string | Your app role, usable in policies. |
data | object | Any extra fields your tools or policies read. |
_ts | number | Unix seconds, added at sign time. Signed mode only. |
_sig | string | HMAC-SHA256 hex over the canonical identity. Signed mode only. |
Where the identity travels depends on the route, and the pattern is not guessable from the HTTP method. There is no fallback: a route reads exactly one location, and an identity sent anywhere else is treated as absent. This table is the whole matrix.
| Field | Type | Description |
|---|---|---|
POST /v1/chat | body | user_context object in the JSON body. |
POST /v1/feedback | body | user_context object in the JSON body. |
PUT /v1/credentials | body | user_context object in the JSON body, alongside name and value. |
POST /v1/scheduled-tasks/seen | body | user_context object in the JSON body. |
PATCH /v1/scheduled-tasks/{task_id} | body | user_context object in the JSON body, alongside status. |
POST /v1/oauth/mcp/start | body | user_context object in the JSON body, alongside server_id. |
POST /v1/knowledge | form | user_context as a multipart form field holding a JSON string, alongside file. |
GET /v1/history | header | X-Era-User. |
GET /v1/scheduled-tasks | header | X-Era-User. |
GET /v1/oauth/mcp/servers | header | X-Era-User. |
DELETE /v1/sessions/{session_id} | header | X-Era-User. The one DELETE that reads the header rather than the query param. |
GET /v1/knowledge | query | user_context query param, URL-encoded JSON. |
GET /v1/credentials | query | user_context query param, URL-encoded JSON. |
DELETE /v1/knowledge/{doc_id} | query | user_context query param, URL-encoded JSON. |
DELETE /v1/credentials/{name} | query | user_context query param, URL-encoded JSON. |
DELETE /v1/scheduled-tasks/{task_id} | query | user_context query param, URL-encoded JSON. |
DELETE /v1/oauth/mcp/connection/{server_id} | query | user_context query param, URL-encoded JSON. |
The X-Era-User header holds the same JSON, URL-encoded. Encode it even when the identity is pure ASCII: a raw JSON value is accepted, but a name with a non-Latin-1 character in it will be rejected by the browser before the request leaves.
X-Era-User: %7B%22id%22%3A%22u_42%22%2C%22_ts%22%3A1735689600%2C%22_sig%22%3A%22...%22%7D
Content types
Requests with a JSON body send Content-Type: application/json. Knowledge upload is the one exception, it is multipart/form-data.
Most responses are JSON. The chat endpoint is different: it always answers with text/event-stream (Server-Sent Events).
That is not negotiated. POST /v1/chat never reads your Accept header, and there is no way to ask it for a single JSON reply — whatever you send, the response is a stream you have to read to the end. The examples include the header below because it states what you expect, but sending it or omitting it makes no difference to what comes back.
Accept: text/event-stream
Endpoints
Each endpoint has its own page with request fields, responses, and copy-paste examples.
/v1/chatSend a message, stream the agent reply and tool activity over SSE.GET/v1/historyFetch prior messages for a session.DELETE/v1/sessions/{id}Delete a session and its history.POST/v1/feedbackRecord a thumbs up or down on a reply.PUT/v1/credentialsStore a per-user secret in the encrypted vault.POST/v1/knowledgeUpload a document to a user knowledge base.GET/v1/scheduled-tasksThe My-tasks panel: list, mark seen, update, cancel.POST/v1/oauth/mcp/startStart a per-user MCP OAuth connection.GET/v1/project-configBootstrap config the widget loads on start.When something goes wrong, the response carries a stable error code. The full list lives on the errors reference.