Browse the manual
External client HTTP and MCP reference
MCP and HTTP tools, scoped access, exact monetary strings, invoice approvals and typed failures.
On this page
For: developers connecting an MCP or HTTP client. First set up an owner-granted connection.
The MCP server and HTTP API use the same tools, grants and business actions. Access starts off. A connection needs owner approval.
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 |
|---|---|
| Tool request body | 256 KiB |
| Requests per connection | 60 per minute |
| Requests per IP address | 60 per minute, shared by clients behind that address |
| Record list page | 1–50 records; default 25 |
| Search text | 100 characters |
| Connection expiry | 1–30 days |
| Invoice lines | 1–100 |
| Invoice approval | 10 minutes from preparation |
Responses use Cache-Control: no-store. Record lists return data and next. Send next as the next request's after cursor.
A null cursor means the last page. Record lists order by ID and disclose no total count.
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 tool discovery and calls. It does not expose MCP resources or prompts. Use tools/list for the granted tool schemas.
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.
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. Made with 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.
Endpoints
GET /api/v1/tools lists granted tools from modules that are on. Each entry gives its HTTP method, path, scope and input schema.
Calls also need the delegated user's current permissions. A listed grant does not establish that every record or action is available.
| HTTP method and path | MCP tool | Scope |
|---|---|---|
GET /api/v1/customers |
customers_search |
customers:read |
GET /api/v1/documents |
documents_list |
invoices:read |
GET /api/v1/documents/{id} |
documents_get |
invoices:read |
POST /api/v1/documents/preview |
documents_preview |
invoices:read |
POST /api/v1/drafts |
drafts_create |
invoices:prepare |
PUT /api/v1/drafts/{id} |
drafts_update |
invoices:prepare |
POST /api/v1/documents/{id}/issue-approvals |
documents_issue_prepare |
invoices:execute |
GET /api/v1/approvals/{id} |
approvals_get |
invoices:execute |
POST /api/v1/approvals/{id}/execute |
documents_issue |
invoices:execute |
GET /api/v1/people |
people_list |
people:read |
GET /api/v1/people/{id} |
people_get |
people:read |
GET /api/v1/teams |
teams_list |
people:read |
Existing connections with individual pilot capabilities keep those capabilities. New connections use the scope groups shown in Permitted access.
Customer and document lists accept search, limit and after. For example, use /api/v1/customers?search=Acme&limit=25.
Customer search returns active permitted IDs and names. Document lists return summaries; an individual read adds public notes, reference and line fields.
Summaries include type, status, number, draft code, customer ID, dates, currency, lock_version, monetary strings, connection ID and href.
A new draft has no document number. Internal notes, customer addresses, bank details, credentials and signed download links stay outside document responses.
People lists accept search, limit, after and optional team_id. Team lists accept search, limit and after.
People reads cover active staff, excluding guests. Responses include name, work email, job title, skills, teams, manager and time zone.
Individual people reads also include weekly working hours. They exclude private contact details, rates, time-off records and sign-in settings.
Team reads include live teams, descriptions, lead IDs and active staff member IDs.
These tools do not send documents, record payments, export files or administer taxes and bank accounts. No item-catalog or full-ledger endpoint exists.
Draft fields
Create and preview require customer_id, currency_code, issue_date, due_date and line_items. Use an active permitted customer and a supported currency.
Dates use YYYY-MM-DD. The due date cannot precede the issue date.
Optional fields are language, notes and reference. Notes allow 5,000 characters; reference allows 100. Language uses a supported code.
Update also requires the current integer lock_version and an editable invoice. Every draft write sends the full header and line set.
| Line field | Format and limit |
|---|---|
description |
Required text, 1,000 characters |
quantity |
Required positive string, dot decimal, at most 4 decimal places |
unit_price |
Required unsigned minor-unit string, at most 18 digits; no separators or leading zeros except 0 |
unit |
Optional text, 20 characters |
discount_type |
Optional percent or amount |
discount_value |
Required when a discount type exists; unsigned string |
tax_rate_id |
Optional ID of an existing active tax rate |
For percent, discount_value uses basis points: 1000 means 10%. For amount, it uses the invoice currency's minor units.
Ask a permitted person for the intended tax rate ID. These tools cannot create rates or decide your tax treatment.
Money is exact. A price of "10001" means EUR 100.01, USD 100.01, JPY 10,001 or BHD 10.001.
PHP selects the currency scale and calculates totals. Send money as JSON strings. Do not send floats or numeric money values.
Do not submit totals, status, creator, document type, bank details, legal fields or frozen tax snapshots. Unknown draft fields fail validation.
Example: preview then save
Use a customer ID from the customer endpoint. This example uses no tax rate. Confirm the correct treatment before issuing.
{
"customer_id": "<permitted-customer-ulid>",
"currency_code": "USD",
"issue_date": "2026-10-08",
"due_date": "2026-11-08",
"reference": "Northwind October",
"line_items": [
{"description": "Website design for October", "quantity": "1.25", "unit_price": "10001"}
]
}
- POST the JSON to
/api/v1/documents/preview. - Check
data.grand_totalof"12501", or USD 125.01. - POST the JSON to
/api/v1/draftswithIdempotency-Key: northwind-october-1. - Open the returned
data.href. - Review the saved draft in Made with Pepper.
For an update, PUT the full JSON with the latest lock_version to /api/v1/drafts/{id}. Use a new key for that operation.
MCP uses documents_preview, followed by drafts_create. Add idempotency_key to the draft call's arguments.
Issue after approval
These three tools need invoices:execute and the delegated user's invoices.finalize permission. They handle one invoice at a time.
Execution also rechecks invoices.manage in the issue action. A custom role needs both invoice permissions and access to that invoice.
For version 0, the preparation body is {"lock_version": 0}. Use the draft's actual version.
- Read the saved draft and its latest
lock_version. - Create a JSON body with that
lock_version. - POST the body to
/api/v1/documents/{id}/issue-approvalswith a newIdempotency-Key. - Show the returned
data.review_urlto the delegated person. - Retain
data.approval_idanddata.noncefor execution. - Read
/api/v1/approvals/{approval_id}for the decision. - If its status is
approved, execute the request within its window.
For execution, POST {"nonce": "<returned-nonce>"} to /api/v1/approvals/{approval_id}/execute. Send a new Idempotency-Key header.
MCP uses documents_issue_prepare, approvals_get and documents_issue. Their id is the document ID for preparation and approval ID afterward.
The preparation response has HTTP 202. It includes the customer, dates, exact totals and expiry. It does not issue anything.
Only the delegated person can click Approve issue or Reject in the app. Another staff member cannot approve through the review link.
Approval lasts 10 minutes from preparation. The decision does not restart the window. approved permits execution; executed confirms issuance.
Execution assigns the next invoice number and freezes the invoice. It sends no email and records no payment.
The result includes the issued document and number. Treat only that result or the issued record as proof of issuance.
A changed draft, expired request, revoked connection or lost permission can prevent execution. Prepare a new request when the previous one is stale.
An identical retry of executed approval returns its stored result. It must use the same connection, nonce and execute key.
Idempotency and conflicts
Each write needs an idempotency key of 1–80 ASCII letters, digits, underscores or hyphens. An identical retry returns its original response.
Keep the JSON values and field order identical. Reusing a key with another payload or target returns 409.
A receipt describes the original write. Read the document for its current status and version. A stale update changes nothing.
Keep your input after a conflict. Read the latest version, review the differences and use a new key. Failed draft transactions store no success receipt.
Executed approvals retain a separate result. A new execute key on the same approval returns approval_used.
Revocation and current permission checks still apply to retries. A retained receipt does not restore lost access.
Errors
HTTP tool errors contain error.code. Validation returns error.fields, a list of field paths. Responses exclude exception messages and credentials.
| Status | Code | Recovery |
|---|---|---|
| 401 | unauthenticated |
Check expiry, revocation, the delegated user and application URL |
| 403 | forbidden |
Check HTTPS, host/port, Origin, grants, current permissions and demo mode |
| 404 | not_found |
Access is off, or the route/record is missing or outside scope |
| 409 | conflict |
Read the latest version or correct a reused key |
| 409 | approval_pending |
Wait for the delegated person's decision |
| 409 | approval_rejected |
Respect the decision; review the draft before any new request |
| 409 | approval_stale |
Read the draft and prepare a new request |
| 409 | approval_used |
Retry with the original execute key to retrieve the result |
| 410 | approval_expired |
Prepare a new request and obtain a new decision |
| 413 | payload_too_large |
Send a tool body of 256 KiB or less |
| 415 | json_required |
Send the JSON content type |
| 422 | validation_failed |
Correct the named fields; use a new key for changed input |
| 429 | rate_limited |
Wait for the minute window to clear; reduce request frequency |
| 503 | unavailable |
Keep the input; retry with the same key if the earlier write may have succeeded |
A hidden record and a missing record produce the same 404. Another connection's approval or an incorrect nonce also returns 404.
After an execution conflict, read the approval and document. Do not assume the client issued anything from a prepared or approved state.
Revocation and retained evidence
Revoking a connection stops its future calls. Turning agent access off revokes all connections. Turning browser sign-in off revokes browser connections.
Expiry, user deactivation, session-revoking permission changes, owner transfer or a changed application URL can also stop access.
Saved drafts, issued originals, connection metadata, request receipts, approval results and audit evidence remain. Revocation cannot erase a client's saved copies.
Read connection setup and privacy boundaries.
Need help with the product?
Contact support