Connect an API or MCP client
Use an approved isolated tenant first. These instructions describe implemented protocols, not permission to enable an integration or a claim of provider certification. The versioned reference contains only reviewed external contracts; the internal OpenAPI inventory is not a partner API.
Register the right client
Section titled “Register the right client”An authorized administrator opens BackOffice → Devices & integrations → API access → Register API client. Select an active integration registry entry, only the required data scopes and explicit locations, then review the grant. An inactive registry entry cannot receive access. Personal-data scopes need a separate PII approval; organization-wide customer access is not a location-local permission. Inspector profiles are separately time/date/location/report bounded and cannot be mixed with ordinary scopes or MCP.
Choose the client type for its actual purpose:
- App with staff sign-in (
public_pkce): a staff member approves a delegated connection. Required for MCP; no client secret. Register the client’s exact redirect URI. HTTPS is required except supported localhost loopback callbacks; wildcards and fragments are not allowed. - Server-to-server (
confidential_machine): a server exchanges its client ID and secret usingclient_credentials. It cannot substitute for a delegated MCP connection. - API key: an opaque key sent as
Authorization: Bearer <key>, not anX-API-Keyheader. Anyone holding it can exercise its remaining grant.
Secrets are revealed once. Store them in your integration’s secret store, not source control, browser storage, URLs, screenshots or logs. A replay of client creation does not reveal a previously returned secret again; rotate a lost one. Some separately approved connector scopes are bounded writes, not general write access. Read each scope’s consent description; a client type never grants every HTTP method or internal route.
For MCP choose analytics.mcp and only the necessary data scopes, such as
reports.read. The granting staff member must retain the live report capability
and location access. Answers leave Butterchop for the AI provider you selected,
under that provider’s terms. Do not approve data your organization cannot share.
Discover the tenant-bound resource
Section titled “Discover the tenant-bound resource”Copy the MCP server URL from the registered client’s details, or obtain the
assigned tenant HTTPS origin and organization resource from your administrator.
Do not use the account/billing host or guess an organization from a request body.
The resource is the issuer’s exact /v1/orgs/{orgId} URL, not its /mcp suffix.
All identifiers and domains below are synthetic. Replace them with the approved isolated tenant’s values before running a request; the example domain does not host a Butterchop tenant.
RESOURCE='https://tenant.example.test/v1/orgs/10000000-0000-7000-8000-000000000001'curl --fail-with-body --silent --show-error \ "$RESOURCE/.well-known/oauth-protected-resource"curl --fail-with-body --silent --show-error \ "$RESOURCE/.well-known/oauth-authorization-server"Validate the discovered issuer, resource and HTTPS origin against the tenant you
intended to connect. An MCP 401 also supplies resource_metadata in
WWW-Authenticate. Follow that trusted metadata to the authorization server;
never send a token to an arbitrary URL copied from untrusted content.
Complete staff consent with PKCE
Section titled “Complete staff consent with PKCE”Use an OAuth-capable MCP client or a reviewed OAuth client implementation:
- Generate a fresh, unpredictable verifier and state in the client. Use
code_challenge = BASE64URL(SHA256(verifier))andcode_challenge_method=S256. - Open the discovered
authorization_endpointwithresponse_type=code, yourclient_id, the exact registeredredirect_uri, requested space-separated scopes, state, challenge, explicit location selection and the discoveredresource. The configured staff consent surface signs in the staff member and displays the requested grant. Do not automate approval with an owner’s password or treat a details response as consent. - The staff member reviews the client, scopes and locations and chooses Approve or Deny. At the callback verify state, issuer and the exact expected redirect before accepting a code or error. A code is single-use, not an API token.
- POST form-encoded data to the discovered
token_endpointwithgrant_type=authorization_code,client_id,code, the sameredirect_uri,code_verifierandresource. A public client sends no secret. Repeated form parameters are refused. Keep returned access and refresh tokens private.
If the tenant has not configured an interactive consent surface, ask its administrator to configure one; do not silently approve via an administrative API. Only the consented intersection of client scope, staff authority and location grants is usable. A successful login alone is not consent.
For a confidential server client, use form-encoded grant_type=client_credentials
and the intended resource at the discovered token endpoint with its configured
client authentication method. Request only granted scopes. This does not acquire
staff delegation or unlock MCP.
Read the consent copy for report, finance, promotion and staff scopes
Section titled “Read the consent copy for report, finance, promotion and staff scopes”The staff consent surface shows each requested scope with the description that the server holds for it. The table gives the meaning of each scope that the report, finance, promotion and staff MCP tools use. The staff member approves only what the table lists. Each scope is read-only. No scope lets the client change data.
| Scope | Tool | What the staff member approves | What the scope never includes |
|---|---|---|---|
reports.read |
list_reports |
The AI client can read the names, versions, inputs and output columns of the reports that the staff member can run. | Report data, and the capability names behind a report. |
tax.read |
get_tax_report |
The AI client can read tax collected, refunded and net, by tax name and rate, for up to 92 days at the granted locations. | A filed return, a compliance certificate, customer tax ids and exemption documents. |
finance.statements.read |
get_profit_and_loss |
The AI client can read the profit and loss statement from an accountant-approved trial-balance snapshot. The statement covers a legal entity. The client can read it only when every location of that entity is granted. | Imports, approvals, journal changes, and an estimate when no snapshot exists. |
promotions.read |
list_coupons |
The AI client can read coupon and automatic promotion terms, validity windows and usage counts at the granted locations. | Redemptions, orders, guest details, staff notes, and any change to a promotion. |
staff.performance.read |
get_staff_performance |
The AI client can read closed checks, net sales and refunds for each staff member who owns the checks, with names and ids, for up to 92 days at the granted locations. | Tips, hours, pay, scores and notes. The tool does not rank or judge a person. |
Before you approve, check these rules:
- Each tool also needs
analytics.mcpand a live staff capability. The granting staff member must still hold that capability at each call. A role does not needreport.zfor the endpoint. Any capability that backs a tool is enough, and the client then sees only the tools that capability allows. staff.performance.readis employee data. A holder ofpii.approvemust approve the client first. The granting staff member needsreport.zandlabor.manage. Without the approval, the tool is not listed and a call is refused.- Answers leave Butterchop and go to the AI provider that you chose. Do not approve data that your organization cannot share with that provider.
- The names of staff, reports, accounts and coupons are data that staff wrote. The AI client must not treat them as instructions.
Read the consent copy for the delivery estimate scope
Section titled “Read the consent copy for the delivery estimate scope”The delivery.quote scope lets a partner client ask for a delivery fee
estimate at the granted locations, through POST /v1/partner/delivery/quote
(action partner.delivery_fee_estimate).
| Scope | Action | What the staff member approves | What the scope never includes |
|---|---|---|---|
delivery.quote |
partner.delivery_fee_estimate |
The client can read non-binding delivery fee and configured feasibility estimates at the granted locations. | A stored address, an order, a payment, a courier booking, a binding price and a guaranteed delivery time. |
Before you approve, check these rules:
- The estimate is not a promise. The order admission calculates the fee and the delivery window again, so a revision or an expiry time never locks a fee or a time.
- The optional postcode is used only to match a configured delivery zone. The server does not store it and does not send it back. A postcode match is zone feasibility, not courier or street-address deliverability.
- The calls share per-client rate limits. When the limiter is not available, the server refuses the call.
Read the consent copy for the partner order scopes
Section titled “Read the consent copy for the partner order scopes”The orders.write scope lets a partner client create a pickup order through
POST /v1/partner/orders (action partner.orders_create) and cancel it through
POST /v1/partner/orders/cancellations (action partner.orders_cancel). The
orders.read scope lets the client look up its order through
GET /v1/partner/orders (action partner.orders_lookup). Only a delegated grant
can create an order. The granting staff member is recorded as the opener.
| Scope | What the staff member approves | What the scope never includes |
|---|---|---|
orders.write |
Create orders at the granted locations: items, modifiers, guest name and phone, external reference. Cancel an order by external reference before it has a payment. Start a card checkout that the guest completes on the payment provider page. | Prices, totals, approvals or training state, a claim that an order is paid, a delivery order, and other locations. |
orders.read |
Read orders at the granted locations, including guest names, seat notes and card last four digits. | Full card numbers and orders at other locations. |
Guest data handling:
- An order that you create through the Partner API keeps the guest name and phone number that you send. Butterchop stores them on the order. Staff of the granted locations can see them.
- Butterchop does not put them in business events. Business events carry only identifiers, a revision and a resource link.
- Send guest data only when the guest agreed to share it.
- The tenant retention rules for order data apply to this data.
Initialize MCP and call a permitted tool
Section titled “Initialize MCP and call a permitted tool”ACCESS_TOKEN below means a token held privately by your test client. Do not
paste a live token into a shared terminal, documentation, shell history or chat;
disable shell tracing and use the client secret store for real integrations.
curl --fail-with-body --silent --show-error "$RESOURCE/mcp" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":"init-1","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"Example client","version":"1.0.0"}}}'Use the returned negotiated protocol version on subsequent requests:
curl --fail-with-body --silent --show-error "$RESOURCE/mcp" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -H 'MCP-Protocol-Version: 2025-11-25' \ --data '{"jsonrpc":"2.0","id":"list-1","method":"tools/list","params":{}}'curl --fail-with-body --silent --show-error "$RESOURCE/mcp" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -H 'MCP-Protocol-Version: 2025-11-25' \ --data '{"jsonrpc":"2.0","id":"locations-1","method":"tools/call","params":{"name":"list_locations","arguments":{}}}'Choose a location returned by list_locations, not a location guessed from
another tenant. Call get_sales_summary with that location_id and either omit
both dates or supply both from and to as real inclusive local business days.
The generated reference contains the exact strict argument schemas and limits.
Unknown arguments, impossible dates and overlong ranges are errors. Money stays
an exact decimal string in its location currency; never add different currencies.
Check the HTTP status and JSON-RPC error and result.isError. HTTP 200
can contain a refused tool result, exhausted quota or failed query. For successful
analytics use structuredContent, its quality_state and citation’s period,
source path and freshness. Distinguish complete, partial and no-data results;
missing data is not zero. Tools/list is grant-filtered and is not a promise that a
later call remains authorized after role, location or client revocation.
HTTP errors, retries and revisions
Section titled “HTTP errors, retries and revisions”Every reviewed HTTP action names its scope, live capability, principal kind and
location source. Header-scoped actions use X-Location-Id; query-scoped actions
use their documented query field. Stored exports authorize their frozen resource
locations, and MCP validates each tool’s location argument. None accepts an
arbitrary posted organization as authority.
| Result | Meaning and safe next action |
|---|---|
| 400 / JSON-RPC invalid params | Correct the request or strict tool arguments; do not retry unchanged. |
| 401 | Wrong token kind, expired/revoked token or resource binding; reauthorize only through the intended issuer. |
| 403 | Scope, current permission, origin, location or approved resource is insufficient. Do not switch to an administrator token as a fallback. |
| 409 | For a documented mutation, refetch its revision or correct a changed-payload idempotency conflict; preserve the user’s draft. |
| 413 / 415 | Reduce the body or use the required content type. |
| 429 | Respect Retry-After and the returned RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset headers. |
| 503 | Limiting infrastructure or the feature is unavailable; fail closed and respect Retry-After. |
Only use Idempotency-Key and If-Match where the operation documents them.
Reuse a key for an unchanged retry after a lost response; a changed request needs
a new intent/key. Never retry an unknown payment or connector result through a
different tender/provider to manufacture success. A 202 acknowledges receipt,
not downstream application. Staff/admin/tender paths are denied to API clients
even if a guessed scope or HTTP verb appears to match an internal schema.
Rotate and revoke
Section titled “Rotate and revoke”Honor token expires_in. Public clients rotate refresh tokens using the
form-encoded refresh grant and the same resource; replace the refresh token
atomically. Do not have parallel refresh requests reuse an old token: reuse
revokes its token family. A confidential client secret rotation has a returned
old-secret expiry; migrate the integration before that bound, not after an
assumed grace period.
Revoke the token at the discovered revocation_endpoint using that client’s
configured authentication, or have the administrator revoke the client in API
access. A public client supplies client_id, token and resource, with no
secret. Token revocation is deliberately non-enumerating; its 200 response is not
proof that an arbitrary token existed. Verify the next protected call is refused.
Client revocation and loss of granting-staff authority also cut off future calls.
Rolling back documentation never revokes credentials.
Verify signed webhook deliveries
Section titled “Verify signed webhook deliveries”An administrator configures Business webhooks separately from the API grant. Endpoint ownership, allowed event types, signing-key references, active client scopes and HTTPS egress policy all have to pass. Do not use an API-client secret as the webhook signing key or disclose the configured secret reference’s value.
Verify the exact raw UTF-8 request body before parsing or acknowledging it:
- Read single-valued
x-butterchop-timestamp,x-butterchop-key-id,x-butterchop-content-sha256andx-butterchop-signatureheaders. - Require an active or explicitly overlapping key ID, a timestamp within
±300 seconds, a lowercase SHA-256 digest and
v1=followed by 64 lowercase hex characters. Compute SHA-256 over the unmodified body bytes. - Compute
HMAC-SHA256(secret, timestamp + '.' + key_id + '.' + body_digest). Compare the validated equal-length digests in constant time. Reject changed body bytes, an unknown key or an expired timestamp. Never fetch a signing key from a URL supplied in the request. - For the signed ownership challenge, echo the exact verified nonce in
x-butterchop-webhook-challenge. A 2xx without that receipt is not ownership proof. The shared transport’s challenge type ismenu.nutrition.endpoint_verification, including for business subscriptions. - For event envelopes deduplicate
id, track each subject’s revision and ignore older revisions. Persist receipt durably before returning 2xx. Refetch an authoritative resource only under its approved grant and documented contract; do not blindly follow arbitrary envelope URLs. Mark applied only after the consumer’s own transaction commits.
Training events do not become external production deliveries. A green test-send receipt proves only the tested receiver/signature path, not every provider, consumer application, production deployment or future delivery.