Pagerox

Developers

The same employees, over an API

Everything an employee does in Slack or the console is available over REST and MCP: sessions, cited answers streamed token by token, tools behind the same permission ladder, and signed webhooks out. API sessions appear in the console history and audit like any other surface, because they are the same surface.

Authentication

Create an API key in Settings, Developers. Keys are shown once and stored hashed; revocation is immediate. Send it as a Bearer token.

curl https://app.pagerox.com/api/v1/sessions \
  -H "Authorization: Bearer pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"employee_id": "EMPLOYEE_ID"}'

Requests are rate limited per key origin (60 per minute). A 429 carries a Retry-After header.

Endpoints

POST /api/v1/sessions

Start a session with an employee ({employee_id}).

POST /api/v1/sessions/:id/messages

Send a message ({content}, optional {images}); returns the cited answer.

GET /api/v1/sessions/:id/messages

Read the session history.

GET /api/v1/employees/:id/export

Download the portable employee bundle.

POST /api/v1/employees/import

Import a bundle; creates a builder draft to review.

POST /api/v1/mcp/employees/:id

The employee as an MCP server (Streamable HTTP, stateless).

Talking to an employee

POST /api/v1/sessions/:id/messages
{"content": "What is our refund policy for annual plans?"}

200 OK
{
  "message_id": "…",
  "content": "Annual plans are refundable within 30 days… [1]",
  "citations": [{ "n": 1, "title": "Refund policy", "source": "…" }],
  "refusal": false
}

Employees answer from your org brain with citations, or say they do not know. That contract holds on every surface, including this one.

A message can carry up to 4 images (PNG, JPEG, WebP, or GIF, 10MB each) alongside the text, each as an https URL or a base64 data URI. Images are validated server-side by content, not filename; a bad image returns 400 with error image_invalid.

{"content": "What does this dashboard show?",
 "images": [
   {"url": "https://example.com/dashboard.png"},
   {"data": "data:image/png;base64,iVBORw0KGgo..."}
 ]}

Every employee is an MCP server

Point any MCP host at an employee and it appears as two tools: ask (a question or a task, with an optional session_id to continue the conversation) and history (read a session back). The transport is Streamable HTTP and the server is stateless, so there is no session to open or tear down: one POST, one turn.

{
  "mcpServers": {
    "remy": {
      "type": "http",
      "url": "https://app.pagerox.com/api/v1/mcp/employees/EMPLOYEE_ID",
      "headers": { "Authorization": "Bearer pk_live_..." }
    }
  }
}

An agent that connects is a caller, not an owner. Every ask is the same turn a person starts in the console: the same plan and budget gate, the same guardrails firewall, the same permission ladder, the same traces and audit ledger. An operation that needs a human approval still parks one, and the answer says so; nothing over MCP can approve it, widen a grant, or raise a cap. The tool set is those two verbs, and neither writes configuration.

Keys are workspace-wide, so the endpoint checks on every single call that the employee belongs to the key's workspace. A key from another workspace gets 404, a revoked key 401, an archived employee 410.

Streaming a turn

Send Accept: text/event-stream to the messages endpoint and the same call answers as a stream instead of a single JSON body: the answer token by token, a progress line per step, a block per tool call, and a final part carrying the authoritative persisted content and its citations. Without the header the JSON response is unchanged. Either way it is one turn per request, so a stream buys no extra budget.

curl -N https://app.pagerox.com/api/v1/sessions/SESSION_ID/messages \
  -H "Authorization: Bearer pk_live_..." \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{"content": "What is our refund policy?"}'

data: {"type":"data-progress","data":{"label":"Searching the brain"}}
data: {"type":"text-delta","delta":"Annual plans are refundable"}
data: {"type":"data-final","data":{"content":"…","citations":[…],"refusal":false}}
data: {"type":"finish"}

Webhook tools

Register an endpoint of yours as a tool an employee can call: one operation, a JSON Schema for its input, and a signing secret shown once. The tool goes through the exact same grant and action-level gate as catalog tools; a call at ask first parks an approval before your endpoint ever hears about it. Private-network targets are rejected.

POST https://your-endpoint.example.com/tools/create-ticket
x-pagerox-timestamp: 1789...
x-pagerox-signature: v1=hex(hmac_sha256(secret, timestamp + "." + body))

{"title": "Follow up with Acme", "priority": "high"}

Event webhooks

Subscribe an endpoint to workspace events: proposal.created, approval.decided, tool.invoked, loop.opened, loop.closed. Payloads carry metadata only (ids, names, counts), never message content. Deliveries are signed the same way, retried with exponential backoff, and dead-lettered after five attempts; the dead-letter list is visible in Settings, Developers.

The portable employee bundle

Export any employee as pagerox.employee.bundle/v2: the versioned role config, the attributed memory ledger, the eval golden set and the employee's own procedures, as plain JSON you can read, diff, and keep. Importing a bundle creates a draft in the builder for review, never a silent hire, and never moves credentials: connections are always re-made by a human. v1 files (no procedures) still import.

{
  "format": "pagerox.employee.bundle/v2",
  "employee": { "name": "Remy", "title": "Support Specialist" },
  "role_config": { "version": 12, "instructions": { … }, "guardrails": { … } },
  "memories": [ { "content": "…", "author_role": "manager", … } ],
  "golden_questions": [ … ],
  "skills": [ { "name": "Refunds", "description": "…", "body": "1. …" } ]
}

The API surface is intentionally small while the platform is young. If you need an endpoint that is not here, tell us.