ChannektDocs › API reference

API reference

The MCP endpoint, the connection routes behind it, and the conventions every answer follows.

Last updated 4 September 2026

The shape of it

Channekt has two programmatic surfaces, and they are for different callers.

  • /mcp - the Model Context Protocol server. This is the one to use from an AI assistant, and the one this reference is mostly about. It is JSON-RPC 2.0 over Streamable HTTP, authenticated by a connection token you create in the product. See AI assistants and MCP for the tool catalogue.
  • /v1/* - the REST surface the Channekt web application itself uses. It is authenticated by a browser session and is documented here for the routes that manage MCP connections. A general-purpose REST API with its own credentials is not offered yet; if you want one, write to support@channekt.com and say what you would use it for.

Base address: https://api.channekt.com. Everything is JSON, everything is over TLS, and every request may carry an X-Request-Id header holding a UUID, which comes back on the response and appears in our logs.

Authentication

The MCP endpoint takes a connection token, created under Settings › AI connections in Channekt:

Authorization: Bearer ck_mcp_<your token>

A token belongs to one workspace and to one person, carries an explicit list of permissions, expires (ninety days by default, at most a year), and can be revoked on its own without affecting anybody's sign-in. It is shown once at creation; Channekt stores only its SHA-256.

Everything the token can do is a subset of what its owner can do. Removing that person from the workspace, or changing their role, takes effect on the connection's very next request - the membership is read every time rather than trusted from the token.

A request with no token, a token that is not ours, a revoked one or an expired one all get the same answer:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="Channekt MCP"

{
  "jsonrpc": "2.0",
  "id": null,
  "error": {
    "code": -32002,
    "message": "That credential is not valid for Channekt. Create a connection under Settings, AI connections, and use the token it shows you once."
  }
}

The cases are deliberately indistinguishable from outside. Which one it was is on the activity list in Channekt, where you are signed in.

The /v1/* routes take a short-lived access token issued by signing in, in the same Authorization: Bearer header. They are the web application's own surface; a connection token does not work on them, and an access token does not work on /mcp.

The MCP endpoint

POST /mcp takes one JSON-RPC 2.0 message, or an array of them, and answers with one response object, an array of them, or 202 with no body when every message was a notification.

GET /mcp answers 405: this server sends no server-initiated messages, so there is no stream to open. DELETE /mcp answers 204 and does nothing - there is no session to end, because every request authenticates on its own.

Supported protocol versions, newest first:

  • 2025-06-18
  • 2025-03-26
  • 2024-11-05

A client naming a version we do not speak is answered with our newest rather than refused, which is what the specification prescribes.

Initialising:

POST /mcp
Authorization: Bearer ck_mcp_<your token>
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": { "protocolVersion": "2025-06-18" }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": { "tools": { "listChanged": false } },
    "serverInfo": { "name": "channekt-mcp", "title": "Channekt", "version": "1a2b3c4" }
  }
}

tools/list returns only the tools this connection was granted. A tool it cannot use is not in the list, so a model is never offered something it will be refused for.

Calling a tool:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_inventory_discrepancies",
    "arguments": { "limit": 25 }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{ "type": "text", "text": "{ ... }" }],
    "structuredContent": {
      "discrepancies": [
        {
          "listingId": "018f4b1a-0000-7000-8000-00000000a001",
          "channel": "MARKETPLACE",
          "shop": "Demo Trading (Marketplace)",
          "sku": "DEMO-TSHIRT-M",
          "warehouseCode": "MAIN",
          "expected": 12,
          "reported": 4,
          "difference": 8,
          "lastPushedAt": "2026-09-03T18:22:11Z",
          "syncState": "IN_SYNC",
          "lastError": null
        }
      ],
      "nextCursor": null,
      "limit": 25,
      "advertisingMoreThanWeHold": 0
    }
  }
}

The answer appears twice on purpose: structuredContent for clients that read it, and the same object serialised in content for clients that do not. Every identifier in these examples is fictional.

Carrying out a change is always two calls:

{ "method": "tools/call",
  "params": { "name": "propose_price_update",
              "arguments": { "skus": ["DEMO-TSHIRT-M"], "changeByPercent": -10 } } }
{
  "actionId": "018f4b1a-0000-7000-8000-00000000b001",
  "kind": "price_update",
  "preview": "Change 1 price:\n  DEMO-TSHIRT-M: 19.99 -> 17.99 GBP",
  "warnings": [],
  "approvalRequired": true,
  "approvalReason": "This workspace asks a person to agree to every change an assistant proposes.",
  "status": "PROPOSED",
  "expiresAt": "2026-09-04T13:00:00Z"
}

After the merchant has approved it in Channekt, the assistant calls execute_price_update with the actionId and its own idempotencyKey. No other argument is accepted - what runs is the stored plan, revalidated against the state of the moment. A repeated execute with the same key returns the first result rather than doing the work twice.

Managing connections

These are /v1/* routes, reached with a signed-in session, and every one of them needs the apikey:manage permission - which owners and administrators hold.

Route What it does
GET /v1/mcp/scopes The permissions that can be granted, each with a sentence.
GET /v1/mcp/connections This workspace's connections. Never the tokens.
POST /v1/mcp/connections Mints one. The only response that carries a token.
POST /v1/mcp/connections/{id}/revoke Stops it on its next request.
POST /v1/mcp/connections/{id}/rotate Issues a replacement and revokes the old one at the same moment.
GET /v1/mcp/activity What connections have been asked for, allowed or refused.
GET /v1/mcp/policy How far assistants may go in this workspace. Needs org:read.
PUT /v1/mcp/policy Changes it.
GET /v1/mcp/actions/pending Proposals waiting for somebody to agree.
POST /v1/mcp/actions/{id}/approve Agrees to one. Not reachable from an assistant.
POST /v1/mcp/actions/{id}/reject Declines one.
POST /v1/mcp/connections
{
  "name": "Claude on my laptop",
  "scopes": ["mcp:orders:read", "mcp:inventory:read"],
  "lifetimeDays": 90
}
201 Created
{
  "connection": {
    "id": "018f4b1a-0000-7000-8000-00000000c001",
    "name": "Claude on my laptop",
    "tokenPrefix": "AbCdEf12",
    "scopes": ["mcp:inventory:read", "mcp:orders:read"],
    "effectivePermissions": ["inventory:read", "order:read"],
    "canAct": false,
    "createdAt": "2026-09-04T10:00:00Z",
    "expiresAt": "2026-12-03T10:00:00Z",
    "lastUsedAt": null,
    "revokedAt": null,
    "status": "ACTIVE"
  },
  "token": "ck_mcp_EXAMPLE_ONLY_THIS_IS_NOT_A_REAL_TOKEN"
}

Errors

The MCP endpoint answers in JSON-RPC:

Code Meaning
-32700The body is not JSON.
-32600Not a JSON-RPC 2.0 request.
-32601No such method, or no such tool.
-32602 The arguments are wrong. The message names the field.
-32603 A failure, recorded, having changed nothing.
-32001The request was cancelled or timed out.
-32002 Not authenticated, or the connection was not granted this.

An error message is written to be acted on and never carries an internal detail: no database identifiers, no stack traces, no marketplace responses, no customer data. A tool that refuses for a reason the model should read - an unknown SKU, a proposal nobody has approved - answers with isError and a message rather than with a protocol error.

The /v1/* routes answer in RFC 9457 Problem Details, with a reference you can quote to support.

Rate limits and timeouts

Requests are limited per address. The general allowance is 300 a minute; exceeding it answers 429 with the number of seconds to wait. One tool call has thirty seconds to finish; a client that gives up before then has its work abandoned, and anything already queued stays queued.

A message body is capped at 256 KB, which is far more than a tool call needs.

Paging

Every list is bounded at a hundred rows and returns nextCursor, or null when there is no more. Pass the cursor back unchanged; never construct one. The limit you asked for is echoed back, so a request that was clamped is visible rather than silent.

A few tools page by number instead, and return page, pageCount and total. Report the total rather than counting the rows you were given.

Money, dates and units

An amount is an object, not a number: { "minor": 1799, "currency": "GBP", "decimal": "17.99" }. Compute with minor; show decimal. When a channel charged in a different currency, originalMinor and originalCurrency carry what it actually charged - their absence is the statement that no conversion happened.

Dates are ISO 8601 in UTC. Weights are grams, dimensions are millimetres, and quantities are whole numbers. available is what can be sold - on hand less what is promised to orders - and it is the figure to quote.

Webhooks

Channekt receives webhooks from the channels you connect; it does not yet send them to you. An assistant that needs to know how a queued operation ended polls get_channel_status or list_sync_errors. Outbound webhooks are not built, and this page will say so until they are.

Versioning

The MCP protocol version is negotiated per connection. The tool catalogue itself is additive: tools and fields are added, and an existing tool's arguments are not removed or renamed without a new name beside the old one. A change that would break a caller gets a new tool.

The REST surface is versioned in its path. /v1 is what exists.

The serverInfo.version returned by initialize is the build you are talking to, which is what to quote when reporting something.