Skip to main content

Become a merchant

A one-time setup. Do this in the SharePay dashboard (recommended) at /merchant, or via the API.

UK businesses, in pounds

SharePay opens Stripe Standard connected accounts with a country of GB, and splits settle in gbp. The country is fixed rather than derived from anything you send, so a business that cannot complete UK Stripe onboarding cannot be onboarded here yet.

Register

POST /api/merchants/register with { name }. It authenticates a signed-in SharePay session (Authorization: Bearer <supabase token>), not an API key, because it is the call that issues you one. You may also send webhook_url here instead of setting it later.

Returns 201 with { id, name, api_key, webhook_url, webhook_secret }. The API key (sp_sk_...) authenticates your server's hosted-checkout calls. The webhook secret signs the events we send to you, so you can verify they came from SharePay. See Webhooks.

One merchant per user. Registering a second returns 409 {"code":"merchant_exists","error":"You already have a merchant account"}.

Both credentials stay retrievable: the Developers page in your dashboard reveals them, as does GET /api/merchants/credentials for the logged-in owner. Store them as server-side secrets anyway, keep them out of client code and version control, and rotate either one if it is ever exposed. Rotating invalidates the old value immediately.

Rotation is an endpoint as well as a button:

POST /api/merchants/credentials/rotate
{ "target": "api_key" }

target must be api_key or webhook_secret, otherwise 400 target must be 'api_key' or 'webhook_secret'. Returns { target, value } carrying the new secret.

Connect Stripe

POST /api/merchants/connect returns { url }, a Stripe hosted onboarding link for a Standard account. Complete it. When Stripe confirms the account, your merchant flips to stripe_charges_enabled and you can start taking splits.

That link is one-shot and expires in about five minutes, so never store it. If yours goes stale, POST /api/merchants/connect/refresh mints a fresh one for the same account. Calling it before you have started onboarding returns 400 {"code":"no_stripe_account","error":"Start onboarding via /connect first"}.

Check your status any time with GET /api/merchants/me, which returns id, name, webhook_url, allowed_domain, stripe_account_id, stripe_charges_enabled, stripe_details_submitted, stripe_connected_at and created_at. Poll this rather than waiting to hear from us. When the account still looks incomplete, /me reconciles straight from Stripe on read, so it is right even if Stripe's account.updated event lagged or never arrived.

If Stripe cannot verify your business

Stripe Connect can decline for reasons outside your control, and it decides that inside its own hosted flow. SharePay is not notified when that happens, so nobody will contact you about it. If you come back from Stripe and stripe_charges_enabled is still false, contact support.

Our team is notified only when SharePay itself cannot start onboarding, that is when POST /api/merchants/connect fails with a 502. Either way the merchant record you registered is kept, and POST /api/merchants/connect is idempotent: calling it again reuses the same connected account rather than opening a second one.

Limits

Only the routes in the table below are rate-limited, and only those routes carry draft-7 RateLimit-* headers. Everything else answers without them, so read your remaining budget off a limited route rather than off any response. GET /api/merchants/me, POST /api/merchants/connect, PATCH /api/merchants, GET /api/merchants/webhooks/deliveries and POST /api/checkout-splits have no limiter and no headers.

EndpointLimitKeyed byBody on 429
POST /api/merchants/checkout/sessions120 per minuteYour API keyToo many checkout session requests. Please slow down.
POST /api/merchants/credentials/rotate, POST /api/merchants/webhooks/test, POST /api/merchants/webhooks/deliveries/:id/resend5 per 10 minutesYour IP, shared with the routes below itToo many requests. Please try again later.
GET /api/checkout-splits/pay/:token, GET /api/merchants/checkout/sessions/:token60 per minuteCaller IPToo many requests. Please try again shortly.

POST /api/checkout-splits has no rate limit.

The 5-per-10-minutes bucket is the one you meet first while wiring up webhooks, because test sends, resends and key rotations all draw on it and it is keyed by IP rather than by account.

It is wider than those three routes, too. The same limiter guards the public write endpoints POST /api/contactus, POST /api/waitlist and POST /api/partner-inquiries, and the count is per IP across all six. So five requests spread over a contact-form submission and a couple of webhook test sends from the same address will exhaust it just as fast as five rotations.

Errors on the merchant endpoints

These apply across the session-authenticated routes under /api/merchants.

StatusBodyCause
401UnauthorizedMissing, malformed or expired Authorization: Bearer header
403{"code":"consent_required","error":"Consent required", ...}The signed-in account has not accepted the current terms or privacy policy. Accept them in the app or on the website, then retry
404No merchant for this userYou have not registered a merchant
404{"code":"no_merchant","error":"Register a merchant first"}The same, from the Stripe Connect routes
409{"code":"merchant_exists","error":"You already have a merchant account"}Registering a second merchant
400Merchant name is requiredname missing on register
400Invalid webhook_url: <reason>webhook_url failed the URL checks
400{"code":"no_stripe_account","error":"Start onboarding via /connect first"}connect/refresh before connect
502Could not start Stripe onboarding or Could not refresh Stripe onboarding linkStripe rejected the account-link request
500Something went wrong!Unhandled server error