Browse the manual

HTTP, MCP and sign-in

Addresses, authentication, request limits and browser sign-in for developers.

On this page

For: developers connecting an HTTP or MCP client. Start with Agent API and AI clients.

This page covers the staff door at /mcp and /api/v1. The public door that visitors' own AI clients use has other rules and no credential. Read Visitor door for visitors' AI clients.

AI is optional. You confirm invoice issuance in Pepper.

Transport and access

Use the HTTPS scheme and host/port from APP_URL. Use the addresses shown in Settings > Agent connections.

Send Authorization: Bearer <credential> on every tool request. Cookies and URL parameters do not authenticate calls.

HTTP requests need Accept: application/json. POST and PUT bodies need Content-Type: application/json.

Requests with an Origin header fail. Call the HTTP API from a server or command-line client.

A connection acts as one active, non-guest user. Its grants do not add missing user permissions. Record access remains subject to that user's scope.

Limit Value
Default tool request body 256 KiB; the exceptions below apply only to their named tools
Requests per connection 60 per minute
Requests per IP address 60 per minute, shared by clients behind that address
List page 1–100 rows; default 25. View tools and some lists take 25, 50 or 100
Search text 100 characters
Connection expiry 1–30 days
Invoice lines 1–100
Invoice approval 10 minutes from preparation by default; the owner can set 5 to 1440 minutes
Upload request body 12 MiB for upload tools
Webhook preparation request body 2 MiB for automations_webhook_send_prepare; its decoded webhook body remains limited to 256 KiB
Website save or preview request body 8 MiB for the encoded HTTP or MCP envelope; decoded snapshot_json remains limited to 1 MiB
Inline upload (content_base64) 8 MB of file bytes
Open uploads 20 per connection, each valid for 30 minutes
Download link One use, valid for 5 minutes; an MCP resource read returns up to 8 MB

The website exception applies only to websites_save and websites_preview. HTTP uses PUT /api/v1/websites/{id}/draft and POST /api/v1/websites/{id}/preview.

The envelope limit allows for JSON escaping around the snapshot string. It does not increase page, section or decoded content limits. Other website tools keep the default request limit.

Responses use Cache-Control: no-store. Lists return data and next_cursor. Send next_cursor as the next request's cursor. A null next_cursor means the last page. Add include_total=true for the total count. See lists.

When a limit is reached, the answer is 429 with a Retry-After header in seconds.

When demo mode is on, draft writes and invoice issue requests return 403. Permitted reads and previews remain available.

MCP transport

The MCP server address ends in /mcp. It accepts Streamable HTTP POST requests and returns JSON responses.

Use a remote MCP client with JSON responses. This server has no standalone event stream. GET and DELETE return 405 when access is on.

Send one JSON-RPC request or notification per call. JSON arrays and client responses fail. Notifications return 202 with no body.

Authenticate with a pasted connection credential or browser sign-in. Browser sign-in uses the separate owner-controlled switch in Agent connections.

The server supports tools, prompts and resources. tools/list gives the granted tools with their input and output schemas, 50 per page. Follow nextCursor for the next page. A connection with more than 50 tools also gets search_tools: search for a tool by words, then call it by its name.

prompts/list gives the jobs that this connection may run, with their inputs. A prompt describes the tools to call and the approvals to ask for. It grants nothing and runs nothing.

resources/read reads pepper://records/{type}/{id}, such as pepper://records/document/{id}. It returns the same JSON as the type's read tool and needs the same scope. pepper://context returns the same JSON as context_get.

MCP names replace dots with underscores. For example, documents.get becomes documents_get.

For a record-specific MCP call, put id in the arguments. For a writing call, also put idempotency_key in the arguments.

HTTP puts the record ID in the path and the idempotency key in the Idempotency-Key header.

Successful MCP calls return structuredContent with the same data as HTTP. Tool failures set isError and include structured error data.

Authentication and protocol failures can return an HTTP error with a JSON-RPC error. They do not use the tool-result envelope.

Upload and download files

Files never travel inside an ordinary tool call. Pepper uses single-use transfers instead.

Upload. Call the upload tool of the task: files_upload_prepare for Files, purchases_receipt_prepare for a receipt, boards_upload_prepare for card files, a board background or a Trello export, and people_photo_prepare for a profile photo. Send name and either:

  • content_base64: the file bytes, base64-encoded, up to 8 MB. The upload is ready at once.
  • size: the size in bytes. The answer has an upload_url. Send the raw bytes to it with PUT and the same Authorization: Bearer header within 30 minutes. Send each upload URL once.

The answer has an upload_id. Pass it to the tool that uses the file, such as files_upload, purchases_attach_files or cards_attach_files. That tool runs the same checks as Pepper's upload screen, including the file type and size of the task, and uses the upload once. If the tool fails, the upload stays ready, so you can correct the call and retry. An upload that nobody uses expires after 30 minutes. Pepper removes its bytes at the next scheduled cleanup.

Download. Tools such as files_download, documents_pdf, documents_xml and purchases_file return a download_url and a resource_uri, with expires_at. Open the link once, within five minutes, without a bearer header; the link itself is the permission. MCP clients can read pepper://downloads/{token} instead, for files up to 8 MB. When the link opens, Pepper checks again that the person and this connection may still read the file, and records the download in the audit log. A used or expired link returns 410. Ask the tool again for a new link. The file arrives as a download (Content-Disposition: attachment) with X-Content-Type-Options: nosniff and a sandboxed content security policy, so an SVG or HTML file never runs script.

Error code Meaning
upload_expired (410) The upload URL is older than 30 minutes. Prepare a new upload.
upload_used (409) The upload already has its bytes or was used.
upload_too_large (413) The bytes are larger than size or the task's limit.
upload_empty (422) The request had no bytes.
download_used, download_expired (410) The link was opened before, or is older than five minutes.

Browser sign-in

Browser sign-in uses authorization code flow with PKCE S256. The owner checks the client and return address before allowing access.

Endpoint Purpose
GET /.well-known/oauth-protected-resource/mcp Protected resource metadata; the root variant also works
GET /.well-known/oauth-authorization-server Authorization server metadata
POST /oauth/register Register a public client
GET /oauth/authorize Open the owner consent page
POST /oauth/token Exchange a code or rotate a refresh token
POST /oauth/revoke Revoke a token

Discovery identifies {APP_URL}/mcp as the resource. Send that resource when starting authorization or exchanging and refreshing tokens.

An unknown client or unregistered return address gets an error page. Pepper does not redirect that request.

Public clients can use dynamic registration or a supported HTTPS Client ID Metadata Document. Registered clients need 1–5 return addresses.

Return addresses use HTTPS, or HTTP on localhost, 127.0.0.1 or [::1]. They must match the registered address and contain no fragment.

Authorization codes last 5 minutes and permit one exchange. Access tokens last up to 1 hour, limited by the connection's expiry.

Refresh tokens rotate. Reusing a code or a rotated refresh token revokes its connection. A refresh token never authenticates a tool call.

Registration, token and revocation requests have a 16 KiB body limit. Registration allows 10 requests per minute per IP address.

Token and revocation requests share a limit of 30 per minute per IP address. OAuth errors use error, such as invalid_grant.

Follow the client's sign-in flow for ordinary setup. See Connect through browser sign-in.

Need help with the product?

Contact support