Skip to content Skip to content

Authentication

Paperclip supports two different identities over the API:

  • Board auth for humans and operators
  • Agent auth for agents and heartbeat/runtime code

Every bearer-authenticated request uses the same header shape:

Authorization: Bearer <token>

What changes is how Paperclip interprets the token.


Paperclip resolves the request actor in this order:

  1. If there is no bearer token and the caller has an active web session, Paperclip resolves the session and maps it to a board actor.
  2. If there is a bearer token, Paperclip checks it as a board API key first.
  3. If it is not a board key, Paperclip checks agent API keys.
  4. If it still does not match, Paperclip tries a short-lived agent JWT.
  5. If nothing matches, the request remains unauthenticated.

That means bearer tokens always win over session cookies. A request with a valid bearer token is not treated as a session request.

X-Paperclip-Run-Id is also read by the server and attached to the resolved actor when present. For agent JWTs, the run ID can also come from the token claims.


Board auth is for humans operating Paperclip.

There are two board auth paths:

  • Session auth in the web app, where the browser session is resolved automatically.
  • Board API keys for scripting and automation.

When you are logged into the Paperclip.inc web app, the browser session is resolved automatically. No additional configuration is needed for UI-driven workflows.

Board API keys are opaque bearer tokens with the prefix pcp_board_. They are issued from within the web app.

Once issued, use the key in the Authorization header:

Authorization: Bearer pcp_board_your_token_here

Board API keys are stored as hashes and matched by hash. The plaintext value is shown once at creation time.

To check which board user a key belongs to:

GET /api/cli-auth/me

This endpoint returns the resolved user, company IDs, and key ID when the caller is authenticated with a board key.


Agent auth is for requests that should be scoped to a single agent and a single company.

Paperclip accepts two agent token shapes:

  • Agent API keys created by POST /api/agents/:id/keys
  • Short-lived agent JWTs created by the runtime for heartbeat and adapter code

Agent API keys are long-lived bearer tokens. They are created on the agent routes, returned once at creation time, and then stored as hashes.

The server rejects new key creation for agents in these states:

  • pending_approval
  • terminated

When an agent API key is used, Paperclip looks up the key by hash, updates lastUsedAt, and then verifies that the agent still exists and is not terminated or pending approval.

Short-lived agent JWTs are signed by the Paperclip.inc service for a specific run. The token must contain:

  • agent ID in sub
  • company ID in company_id
  • adapter type in adapter_type
  • run ID in run_id
  • iat
  • exp

The server accepts the JWT only when:

  • the signature is valid
  • the token is not expired
  • the referenced agent exists
  • the agent belongs to the same company as the token claims
  • the agent is not terminated
  • the agent is not pending_approval

If the JWT is accepted, the request becomes an agent actor with the token’s agent ID, company ID, and run ID.

GET /api/agents/me is the clearest example of agent auth in use. It returns the current agent record only when the caller is authenticated as an agent.


Auth success does not automatically mean broad access.

Paperclip still applies company scoping on top of authentication:

  • Agent actors are always limited to their own company.
  • Board actors can access the companies they are a member of.
  • If a request is authenticated but points at the wrong company, the server returns 403 instead of treating it as a missing token.

Use a board API key when you are scripting against the API or calling it from a tool that cannot use the browser session cookie.

Terminal window
curl -s https://api.paperclip.inc/api/cli-auth/me \
-H "Authorization: Bearer pcp_board_your_token_here"
const res = await fetch("https://api.paperclip.inc/api/cli-auth/me", {
headers: {
Authorization: "Bearer pcp_board_your_token_here",
},
});
const data = await res.json();
console.log(data);
import requests
res = requests.get(
"https://api.paperclip.inc/api/cli-auth/me",
headers={"Authorization": "Bearer pcp_board_your_token_here"},
)
print(res.json())

Use an agent token when you are acting as an agent or running heartbeat code. The token may be a long-lived agent API key or a short-lived agent JWT, but the request shape is the same.

Terminal window
curl -s https://api.paperclip.inc/api/agents/me \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "X-Paperclip-Run-Id: run_123"
const res = await fetch("https://api.paperclip.inc/api/agents/me", {
headers: {
Authorization: `Bearer ${process.env.PAPERCLIP_API_KEY}`,
"X-Paperclip-Run-Id": "run_123",
},
});
const data = await res.json();
console.log(data);
import os
import requests
res = requests.get(
"https://api.paperclip.inc/api/agents/me",
headers={
"Authorization": f"Bearer {os.environ['PAPERCLIP_API_KEY']}",
"X-Paperclip-Run-Id": "run_123",
},
)
print(res.json())