Skip to main content

API reference

Every endpoint your integration touches, in one place. Each row links to the page that explains it.

Base URLs​

EnvironmentBase URL
Productionhttps://api.share-pay.co.uk
Sandboxhttps://demo.share-pay.co.uk/api

Credentials never cross between them. See Testing.

Authentication​

Three different things authenticate the endpoints below, and mixing them up is the most common integration mistake.

AuthHeaderWhat it reaches
API keyx-merchant-api-key: sp_prod_...Starting a hosted checkout. Nothing else. Use it from your server only: a browser cannot send this header, and the key must never reach a web page anyway
SessionAuthorization: Bearer <supabase token>Everything under /api/merchants and /api/checkout-splits. This is a signed-in person, not your server
None—The lookups a buyer's browser makes. There is no password: the token in the address is the credential, so treat every one as a secret

If your login belongs to more than one business, add x-sharepay-merchant-id: <merchant_id> to session-authenticated calls, or they answer 409 business_selection_required. See Being in more than one business.

Amount units​

There is no single rule. Check the endpoint.

WhereFieldUnit
POST /api/merchants/checkout/sessionsamountPounds. £120 is 120, and 12000 means £12,000
POST /api/checkout-splitsamountMinorPence. £40 is 4000, minimum 100
Webhook payloads, session lookup, list responsesamount, total_amountDecimal strings in pounds, two places
Detail response (GET /api/checkout-splits/:id)amount, total_amountDecimal, unformatted. Parse it, do not assume a shape

Every amount above is a share, before fees. The card is charged the share plus a flat 20p. See what SharePay charges.

Error shape​

Every error is JSON with an error message:

{ "error": "Invalid API Key" }

Some carry a machine-readable code alongside it — merchant_exists, no_merchant, no_stripe_account, consent_required, business_selection_required, business_forbidden, business_not_found, billing_card_required (add a card in Settings, Billing), billing_paused (pay the unpaid SharePay bill in Settings, Billing). Branch on code where there is one and on the status otherwise; never on the message text. Validation failures on registration and invoicing add a details array of { field, message }.

Elsewhere in these docs an error is written as its status and message for brevity; the body is always the object above.

Hosted checkout​

The path a shop integrates. See Hosted checkout.

EndpointAuthRate limitNotes
POST /api/merchants/checkout/sessionsAPI key120/min per keyCreates a session. amount in pounds. Optional line_items itemise the order and must sum to amount. Optional customer_email, customer_name and flat string metadata (20 keys, 500-char values, 4 KB). Send an Idempotency-Key header to make retries safe: the same key returns the same session for 24 hours; the same key with a different body is 409 idempotency_key_reused. Returns { token, checkout_url }. Needs allowed_domain set, and stripe_charges_enabled with a live key (a test key does not need Stripe)
GET /api/merchants/checkout/sessions/:tokenNone60/min per IPWhat the hosted page loads. Public shape only: status, line_items, split_id, customer_email, and mode (test or live)
GET /api/merchants/checkout/sessions/:token/statusAPI key120/min per keyReconciliation. The session with customer_*, metadata, and split (null until the payer chooses): status of pending, paid, canceled or refunded, approved_count, total_count, participants. Another merchant's token is a 404
POST /api/merchants/checkout/splits/:id/refundAPI key120/min per keyRefund a paid order from your server. No amount refunds everything outstanding; an amount is spread pro-rata over the payers to the penny. reason and idempotency_key optional. 409 partial_failure reports a refund Stripe refused part-way, with what went through. See managing splits
POST /api/merchants/checkout/splits/:id/cancelAPI key120/min per keyCancel an order from your server while its split is still pending: holds are released and nobody is charged. 200 {"status":"canceled"}, also when it was already cancelled. Any other status is 409 not_pending with the current status. See managing splits
POST /api/merchants/checkout/sessions/:token/remindAPI key or dashboard session120/min per key or IPEmails every participant still pending. One per participant per 12 h, three calls per session; 429 session_cap_reached after that, 409 nothing_to_remind when there is nobody to chase. Returns { reminded, skipped, next_allowed_at }

Splits​

Session only — your API key authenticates none of these. See Refunds, disputes and order status.

EndpointRolesNotes
POST /api/checkout-splitsOwner, Admin, MemberYou set the split up. amountMinor in pence. No rate limit, and not safe to retry — a repeat makes a second split. Create a split yourself
GET /api/checkout-splitsAllYour splits, newest first. Summary rows only
GET /api/checkout-splits/:idAllThe full record, including stripe_payment_intent_id per participant and disputed_at
POST /api/checkout-splits/:id/cancelOwner, Admin, MemberReleases holds on a pending split. A 200 does not mean anything was cancelled — re-read the split
POST /api/checkout-splits/:id/refundOwner, Admin, FinanceRefunds the whole split, from the dashboard. Only from captured. For partial refunds use the API-key route above
GET /api/checkout-splits/pay/:tokenNone60/min per IP. One participant's own view, for hosting the pay step yourself

Your account​

See Become a merchant.

EndpointRolesNotes
POST /api/merchants/registerAny signed-in accountOne business per login. Returns api_key and webhook_secret
GET /api/merchants/meAllStatus, settings and your business_role. Reconciles from Stripe on read — poll this rather than waiting to hear from us
POST /api/merchants/connectOwner, AdminStripe onboarding link. One-shot, expires in ~5 minutes
POST /api/merchants/connect/refreshOwner, AdminA fresh link for the same account
GET /api/merchants/credentialsOwner, AdminReveals api_key and webhook_secret
POST /api/merchants/credentials/rotateOwner, Admin5 per 10 min per IP, shared. { target: "api_key" | "webhook_secret" }
PATCH /api/merchantsOwner, AdminSets webhook_url and allowed_domain. An empty value clears the field
GET /api/merchants/workspacesAny signed-in accountEvery business this login can reach

Webhooks​

See Webhooks.

EndpointRolesNotes
GET /api/merchants/webhooks/deliveries?limit=50AllDelivery log, newest first. limit clamped to 1–100
POST /api/merchants/webhooks/testOwner, Admin5 per 10 min per IP. Sends a sample checkout_split.paid
POST /api/merchants/webhooks/deliveries/:id/resendOwner, Admin5 per 10 min per IP. Fresh timestamp and signature, same data

Deliveries carry X-SharePay-Signature: t=<unix>,v1=<hex>, which proves the request came from us, plus X-SharePay-Event-Id (stable across retries, dedupe on it) and X-SharePay-Attempt. A delivery that is not acknowledged with a 2xx within 10 seconds is retried eight times over about 47 hours (nine attempts in all), then given up on and the business emailed. v1 is an HMAC-SHA256 over <t>.<raw body>, keyed on your webhook secret. Events: checkout_split.paid, checkout_split.canceled, checkout_split.participant_authorised, checkout_split.refunded, checkout_session.expired. Split events carry session_token, customer_email, customer_name and metadata from the session (all null for a split made on the dashboard). There is no event for chargebacks or a split that failed.

Team​

See Team access and roles.

EndpointRolesNotes
GET /api/merchants/teamAllMembers, pending invites, and your own current_role
POST /api/merchants/team/invitesOwner, Admin5 per 10 min per IP. Invitations last 7 days
POST /api/merchants/team/invites/:id/resendOwner, Admin5 per 10 min per IP. Replaces the previous link
DELETE /api/merchants/team/invites/:idOwner, AdminRevokes a pending invitation
PATCH /api/merchants/team/members/:idOwner, Admin{ role }. Cannot reach the owner
DELETE /api/merchants/team/members/:idOwner, AdminCannot remove yourself
POST /api/merchants/team/transfer-ownershipOwnerNeeds the business name as confirmation. Demotes you to Admin
GET /api/merchants/team-invites/:tokenNoneShows an invitation before sign-in, with the email masked
POST /api/merchants/team-invites/:token/acceptThe invited personSingle use. Must be signed in as the invited address

Rate limits at a glance​

Only these endpoints are limited, and only these send back RateLimit-* headers telling you what is left. Check your remaining budget on one of these, never on any other response.

BucketKeyed byRoutes
120 per minuteYour API keyPOST /api/merchants/checkout/sessions
60 per minuteCaller IPGET /api/checkout-splits/pay/:token, GET /api/merchants/checkout/sessions/:token
5 per 10 minutesYour IP, shared across all of themCredential rotation, webhook test, delivery resend, every team invite route — and the public contact, waitlist and partner forms

That last bucket is the one you meet first, and it is per IP rather than per account. See Limits.