REST API & webhooks
How your own systems talk to LiveContext and how a workflow talks back. This page covers API keys, calling a webhook trigger, the response and error format, share links, chat over the API, and outbound calls with the HTTP Request node.
Overview
There are three ways in and one way out:
- A webhook trigger starts a workflow when your system sends an HTTP request to its URL. No API key is needed; the webhook has its own optional authentication.
- An API key (
lc_live_) lets a script call the authenticated API as you, or connect an MCP client to the MCP server. - A share link gives read or interactive access to one shared resource without a login.
- The HTTP Request node is the way out: your workflow calls an external API.
In the examples below, https://livecontext.ai is the cloud address. On a self-hosted Community Edition install, use the address of your own instance instead.
API keys
An API key looks like lc_live_ followed by 64 hexadecimal characters. You can hold up to 20 active keys, each with its own name, so you can give every script or client its own key and revoke one without touching the others.
| Access | What the key can do |
|---|---|
| Full access | Authenticates every API call as you, on the MCP endpoint and on the rest of the API (for example the chat API). |
| Limited to specific tools | Works only on the MCP endpoint (/mcp), and only for the tools you picked. Any other API path answers 403 with {"error": "scope_forbidden"}. |
Create a key
- Open the key settingsGo to Settings > MCP Server. The API keys section lists your keys.
- Start a new keySelect New key, then type a Name that says where the key is used (for example "Nightly export script").
- Choose the accessLeave Limit to specific tools off for a full-access key. Turn it on to pick tools under Tools; you must pick at least one.
- Copy the key nowSelect New key to create it. The Key created dialog shows the full key once: copy it and store it somewhere safe, then select Done. Afterwards the list only shows a masked hint such as
lc_live_...a1b2.
Revoke a key
In the API keys list, select the Revoke (bin) icon next to the key, then Revoke key to confirm. Revocation is immediate: the next request with that key fails with 401. Your other keys keep working.
Authenticate a request
Send the key in either header. Both forms are accepted everywhere a key is accepted:
X-API-Key: lc_live_xxxxxxxx
Authorization: Bearer lc_live_xxxxxxxxcurl https://livecontext.ai/api/auth/api-keys \
-H "X-API-Key: lc_live_xxxxxxxx"Keys are sent in a header, never in the URL. Treat a full-access key like a password: anyone who has it acts as you.
Manage keys over the API
A full-access key (or your signed-in session) can list and revoke keys directly. Creating one is different on LiveContext Cloud: it needs a sign-in from the last 5 minutes in the app, so an API key can never create another key (the request answers 403 reauthentication_required). Create keys in Settings, which asks you to sign in again when needed.
| Request | What it does |
|---|---|
GET /api/auth/api-keys | Lists your active keys: id, name, maskedApiKey, scopes (null for full access), createdAt, lastUsedAt. Never returns the full key. |
POST /api/auth/api-keys | Body {"name": "...", "scopes": ["table"]}. Omit scopes or send null for full access; an empty list is rejected. Returns the key entry plus apiKey, the full key, shown only in this response. Returns 400 when the name is missing or longer than 100 characters, or when you already have 20 active keys. On LiveContext Cloud, only from a recent sign-in (see above). |
DELETE /api/auth/api-keys/{id} | Revokes the key immediately. Returns 204, or 404 if the key is not yours. |
curl -X POST https://your-install.example/api/auth/api-keys \
-H "X-API-Key: lc_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name": "Reporting client", "scopes": ["table"]}'The tool names you can use as scopes are the ones listed under Tools when you create a key in Settings. See MCP server for what each key can reach there.
Rate limits
| Traffic | Ceiling |
|---|---|
| Free plan | 1,000 requests per minute |
| Starter plan | 2,000 requests per minute |
| Pro plan | 3,000 requests per minute |
| Team plan | 6,000 requests per minute |
| Enterprise plans | 10,000 requests per minute |
| Requests without a user (per IP address) | 600 requests per minute |
| One share link | 60 requests per minute |
| All share links of one owner | 500 requests per day |
Over the ceiling, the response is 429 Too Many Requests with an empty body and a Retry-After header giving the number of seconds until the window resets. Wait that long before you retry.
Errors from the API
Authentication errors come back as JSON with an error and a message field:
{
"error": "Unauthorized",
"message": "Invalid or unknown API key."
}| Status | Meaning | What to do |
|---|---|---|
| 401 | No credentials, or the key is unknown or revoked. | Check the header name and the key; create a new key if it was revoked. |
| 403 | scope_forbidden: a key limited to specific tools was used outside /mcp. | Use a full-access key for REST calls. |
| 429 | Rate limit reached (cloud). | Wait for the seconds given in Retry-After, then retry. |
Call a webhook trigger
A Webhook trigger gives a workflow its own URL, shaped https://livecontext.ai/webhook/{token}, where the token starts with wh_. You find the URL on the trigger in the workflow builder, and every webhook you own is listed in Settings > Public Access, on the Webhooks tab. See Triggers to set one up and Public access & sharing for public URLs, forms, and token regeneration.
A webhook call needs no API key. Each webhook accepts exactly one HTTP method (GET, POST, PUT, PATCH, or DELETE; POST by default). Another method returns 405 Method Not Allowed.
curl -X POST https://livecontext.ai/webhook/wh_xxxxxxxx \
-H "Content-Type: application/json" \
-d '{"orderId": 42, "status": "paid"}'Webhook authentication
| Auth type | How the caller authenticates |
|---|---|
| None | No check (the default). Anyone with the URL can call it. |
| Basic | HTTP Basic auth: Authorization: Basic base64(user:pass). |
| Header | A header name and value you choose, sent with every call. |
| JWT | Authorization: Bearer <JWT>, verified against a secret you configure (HS256 by default, or HS384 / HS512). |
A failed check returns 401 Unauthorized.
Reading the incoming request
The Webhook trigger exposes the request to the rest of the workflow:
| Field | Notes |
|---|---|
| payload | The request body. For a GET request the query string becomes the payload; for other methods the JSON body is used, with query parameters merged in as a fallback. |
| headers | All request headers. |
| query | Query string parameters (alias queryParams). |
| method | The HTTP method of this call. |
| triggered_at | ISO timestamp of the call (alias triggeredAt). |
| triggered_by | Display name of the workflow owner (alias triggeredBy); empty for an unauthenticated webhook. |
What the caller gets back
By default the call is asynchronous: LiveContext acknowledges it and the workflow keeps running. The body is JSON with executionId, status, and message:
{
"executionId": "3f6c2a8e-1b7d-4c55-9e0a-7d1f2b3c4d5e",
"status": "triggered",
"message": "Workflow run triggered successfully"
}| status | HTTP status | Meaning |
|---|---|---|
| accepted, triggered | 202 | The workflow started. |
| completed | 200 | The workflow finished and replied (sync mode, see below). |
| not_found | 404 | No webhook with this token, or it is disabled. The body is empty. |
| not_active | 409 | The workflow has no run waiting for this trigger. |
| insufficient_credits | 402 | The owner has no credits left. |
| rate_limited | 429 | Too many calls to this webhook; wait and retry. |
| method_not_allowed | 405 | The HTTP method does not match the webhook. |
| unauthorized | 401 | The webhook authentication failed. |
| error | 400 | Any other error; message says why. |
Reply from the workflow (Respond to Webhook node)
To have the workflow write the reply itself, add ?sync=true to the URL and place a Respond to Webhook node in the workflow.
curl -X POST "https://livecontext.ai/webhook/wh_xxxxxxxx?sync=true" \
-H "Content-Type: application/json" \
-d '{"question": "order status", "orderId": 42}'| Setting | Default | Notes |
|---|---|---|
| statusCode | 200 | A value of zero or less is replaced by 200. |
| body | Empty | Supports {{...}} templates resolved against workflow data. |
| contentType | application/json | The Content-Type of the reply: one media type, such as application/json or text/plain. Anything else is sent as application/octet-stream. |
| headers | None | Extra response headers. For browser callers, Access-Control-Allow-Origin, -Allow-Methods, -Allow-Headers, -Expose-Headers and -Max-Age are sent. Cookies (Set-Cookie), redirects (Location, Refresh), security policies, Access-Control-Allow-Credentials and connection headers are dropped; the reply is still sent. |
The node outputs responded (false when no sync webhook call was waiting, for example on a manual run), statusCode, and contentType. Because responded=false is not a failure, the same workflow runs safely whether or not it was called in sync mode.
Public share links
Sharing an application or a conversation creates a share link. A share link gives access to that one resource, without a login: a shared application is interactive (visitors use it as you built it), and a shared conversation is read-only. A client presents the token as Authorization: ShareToken <token>. See Public access & sharing to create and revoke links, and Interfaces & apps and Marketplace for publishing.
Chat over the API
There are two chat surfaces, and they are not interchangeable.
Public chat endpoint (no login)
Built for chatbot apps wired to a chat trigger. It is synchronous request and response: no streaming, no stop endpoint.
| Endpoint | Purpose |
|---|---|
POST /chat/{token}/session | Create or resume a session. |
POST /chat/{token}/message | Body {"sessionId": "...", "message": "..."}; returns the reply. |
GET /chat/{token}/history | Needs an X-Chat-Session header. |
GET /chat/{token}/config | The chat endpoint configuration. |
Authenticated chat API (full-access key)
For driving a full conversation from your own backend. POST /api/v3/chat sends a message and returns {conversationId, streamId, model} right away while the agent works. Token and tool events stream over the WebSocket at /ws, not from this REST call. POST /api/v3/chat/stop with {"conversationId": "..."} stops an active answer. A key limited to specific tools cannot call this API (scope_forbidden).
Call external APIs from a workflow (HTTP Request node)
The HTTP Request node makes your workflow the caller. Methods: GET (default), POST, PUT, PATCH, DELETE. The URL, query values, header values, body, and auth values all accept {{...}} templates.
| Auth type | Behavior |
|---|---|
| none | The default; no auth added. |
| basic | Base64(user:pass) sent as Authorization: Basic. |
| bearer | Authorization: Bearer <token>. |
| api-key | A named key sent in a header or a query parameter. |
| custom-header | Any header name and value. |
| Body type | Behavior |
|---|---|
| none | The default; no body. |
| json | Parsed, then sent as JSON. |
| x-www-form-urlencoded | Parsed into form fields. |
| form-data, raw | Sent as is. |
Content-Type is set from the body type when you do not supply it; Accept defaults to application/json, */*. An optional timeout (milliseconds) is capped at 300,000 ms (5 minutes).
| Output | Notes |
|---|---|
| success | false when the server answered with an error status (4xx or 5xx). |
| status | The HTTP status code (alias statusCode). |
| statusText | The status line, for example 200 OK. |
| data | The response body (alias body, response). JSON is parsed; anything else stays a string. |
| headers | The first value of each response header; headersMulti holds every value. |
| error | The error message, if any (alias errorMessage). |
Troubleshooting
For what each status code means, see Errors from the API and the webhook response statuses above. This table starts from what you observe.
| Symptom | Cause | Fix |
|---|---|---|
A webhook call answers 409 with status not_active | The workflow has no pinned production version, or the production run of that version has ended. | Pin a version with Set as production (see Workflows) and make sure its production run is live. See Public access & sharing. |
A call with ?sync=true returns 202 instead of your reply | No Respond to Webhook node answered within 60 seconds: the workflow has none on the path that ran, or it reached it too late. | Add a Respond to Webhook node on every path the call can take, and place it before the slow steps. The workflow keeps running either way. |
A REST call with your key answers 403 scope_forbidden | The key is limited to specific tools, so it only works on /mcp. | Create a full-access key for scripts that call the REST API, and keep the limited key for your MCP client. |
| An HTTP Request node is green, but the next step gets an error message instead of data | A 4xx or 5xx response does not fail the node: it completes with success=false and the error body in data. | Branch on success or status with a Decision node before using data. |
An HTTP Request node fails with Only http and https schemes are allowed (got: <none>) | The URL has no http or https scheme. This usually means a template in the URL resolved to an empty value. | Check the output of the step the URL template points to, and write the full address, including https://. |
An HTTP Request node fails with Requests to localhost are not allowed or Requests to private/internal network addresses are not allowed | Private, loopback, and link-local addresses are always refused. | Expose the service on a public address (for example through a tunnel) and call that address instead. |