Skip to main content

Become a merchant

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

Use the account you already have

A business is added to a SharePay account; it is not an account of its own. If you already use SharePay, sign in with that account and add the business from the profile card in the sidebar. Do not make a second account with a business email: your identity is verified once, on your account, and every business you run sits under it. If you have already done that, the owner of the business can move it to your account from Team, or support can.

UK businesses, in pounds

SharePay sets every business up as a UK Stripe account, paying out in pounds. That is fixed — nothing you send changes it — so if your business cannot complete Stripe's UK checks, we cannot take you on yet.

Register​

POST /api/merchants/register authenticates a signed-in SharePay session (Authorization: Bearer <supabase token>), not an API key, because it is the call that issues you one.

{
"name": "Corner Coffee",
"legal_name": "Corner Coffee Ltd",
"business_type": "limited_company",
"business_address": "12 High Street, Leeds, LS1 1AA",
"business_email": "[email protected]",
"business_phone": "+44 113 496 0000",
"company_number": "12345678"
}
FieldRequiredNotes
nameYesYour trading name. This is what customers see: the checkout header, participant emails, the Stripe charge description
legal_nameYesThe registered name of the entity
business_typeYesOne of sole_trader, limited_company, partnership, limited_liability_partnership, charity, other
business_addressYesThe registered address, as one string. You may send business_address_parts instead, and the text is re-derived from it
business_emailYesA valid address. This is your business contact, not the login
business_phoneYes7 to 30 characters
company_numberConditionalRequired for limited_company and limited_liability_partnership. Optional otherwise
vat_numberNoIf you are VAT registered
webhook_urlNoSet it here instead of later. It goes through the same URL checks

A missing or malformed field returns 400 with an error of Complete the required business details and a details array of { field, message } naming each one.

Returns 201 with { id, name, webhook_url }. No API key and no webhook secret are created here. Make each one under Settings, Developers when you need it.

The API key (sp_prod_..., or sp_test_... in test mode) authenticates your server's hosted-checkout calls. It is shown once, when you create it, and never again: we keep only a hash of it. Copy it straight into your server's environment. If you lose it, revoke it and create another; it cannot be recovered. A business can hold several keys, each with a name, and revoke one without disturbing the rest.

The webhook secret signs the events we send to you, so you can verify they came from SharePay. See Webhooks.

Older keys keep working exactly as before. Keys made before 27 September 2026 look like sp_sk_ followed by hex and are live keys. Keys that start sp_sk_live_ or sp_sk_test_ came before the sp_prod_ and sp_test_ names and keep their mode.

One business per login unless you say otherwise. Registering a second returns 409 {"code":"merchant_exists","error":"You already have a business. Confirm if you meant to add another."}; send allow_additional: true to add one deliberately. Colleagues do not each register: invite them into the business you just made, and they join it with a role. See Team access and roles.

One company number, one business. Registering a company that is already on SharePay returns 409 {"code":"company_already_registered", ...} with the business name; ask its owner to invite you, or contact support if the first registration was the wrong one.

Everything on this page beyond GET /api/merchants/me needs the Owner or Admin role. A Finance or Member colleague cannot create or revoke keys, see the webhook secret, or touch Stripe onboarding.

API keys​

Create a key under Settings, Developers, or with the endpoint below. The whole key is returned once, in that response, and never again: we store only a hash of it. Copy it straight into your server's environment and keep it out of client code and version control.

POST /api/merchants/api-keys
{ "name": "Production server" }

name is optional and helps you tell keys apart later. Returns 201 with the key in key, alongside its id, name, mode, last_four and created_at.

A business can hold several keys. List them with GET /api/merchants/api-keys, which returns each key's id, name, mode, last_four, created_at and last_used_at, but never the key. A lost key cannot be recovered. Revoke it and create another:

POST /api/merchants/api-keys/:id/revoke

Revoking stops that key working immediately and leaves your other keys alone. Anyone who can manage settings can revoke a key at any time, whether or not the business has finished verifying, so a leaked key can always be stopped.

A live key needs your business verified. That means the owner has verified their identity in the SharePay app and, for a limited company or LLP, Companies House confirms the company is active and lists them. Until then, creating a live key returns 403 {"code":"live_key_needs_verification", ...}. The business's verification_status in GET /api/merchants/me reads verified once it is done. A business that was taking payments before verification existed (verification_grandfathered: true) can create a live key without it.

GET /api/merchants/credentials no longer returns an API key, because there is none to return. Asking POST /api/merchants/credentials/rotate to rotate api_key returns 400 {"code":"use_api_keys", ...}.

Webhook secret​

The webhook secret is created on request as well. Create it, or replace it, from Settings, Developers, or:

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

Returns { target, value, mode } carrying the new secret. The first call creates it; a later one replaces it, and the old value stops verifying immediately. With the X-SharePay-Mode: test header it is the test endpoint's secret instead: test and live webhooks have separate URLs and secrets.

Unlike an API key, the webhook secret stays readable, because you need it to verify our signatures: the Developers page shows it, as does GET /api/merchants/credentials, which returns { mode, webhook_url, webhook_secret } for the mode you ask for (live unless you send X-SharePay-Mode: test), with null for anything not yet created. We store it encrypted.

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":"Connect Stripe first."}.

Check your status any time with GET /api/merchants/me. Any role may read it, and the fields that matter for onboarding are:

FieldNotes
stripe_charges_enabledThe flag to gate your checkout on. True once Stripe has enabled charges
stripe_details_submittedStripe has the details, but may not have finished verifying them
stripe_account_id, stripe_connected_atYour Stripe account, and when it was linked
business_roleYour own role in this business: owner, admin, finance or member
platform_fee_bpsYour fee, in hundredths of a percent. 300 is the standard 3%
platform_fee_discount_ends_atWhen a promotional rate reverts to the standard 3%, or null
webhook_url, allowed_domainYour integration settings
id, name, created_atYour merchant
gatesWhat each feature still needs in the mode you asked in (x-sharepay-mode), or null when it is ready: issue_invoice, issue_invoice_bank_transfer, take_split. Each is { code, message, fix: { label, path } }

It also returns the business and invoicing details you registered — legal_name, business_type, business_email, business_phone, the address fields, vat_number and company_number — because the dashboard's Settings page prefills its forms from them.

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 calling POST /api/merchants/connect again is safe: it reuses the Stripe account you already started rather than opening a second one.

Limits​

Only the endpoints in the table below are rate limited, and only those send back RateLimit-* headers telling you how much you have left. Everything else answers without them, so check your remaining budget on a limited endpoint — not on just any response.

These have no limit and no headers: GET /api/merchants/me, POST /api/merchants/connect, PATCH /api/merchants, GET /api/merchants/webhooks/deliveries and POST /api/checkout-splits.

EndpointLimitKeyed byBody on 429
POST /api/merchants/checkout/sessions120 per minuteYour API keyToo many checkout session requests. Please slow down.
POST /api/merchants/api-keys, POST /api/merchants/credentials/rotate, POST /api/merchants/webhooks/test, POST /api/merchants/webhooks/deliveries/:id/resend5 per 10 minutesYour IP address, shared across all of themToo 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 five-per-ten-minutes limit is the one you will hit first, usually while setting up webhooks, because test sends, resends and key rotations all come out of the same allowance.

Two things make it easier to hit than it looks. It counts by IP address, not by account, so everyone in your office shares one allowance. And it also covers our public contact, waitlist and partner forms — so a contact-form submission plus a couple of webhook tests from the same address uses it up just as fast as five key 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 business. Confirm if you meant to add another."}Registering a second business without allow_additional: true
409{"code":"company_already_registered","error":"This company is already on SharePay. ...","business_name":"..."}The company number is held by another business. Ask its owner for a team invite
400{"error":"Complete the required business details","details":[...]}A required registration field is missing or malformed. details names each one
403{"code":"business_forbidden", ...}Your role does not allow it. Seeing credentials, connecting Stripe and PATCH /api/merchants all need Owner or Admin
409{"code":"business_selection_required","error":"Select a business workspace before continuing."}Your login is in more than one business. See Being in more than one business
400Invalid webhook_url: <reason>webhook_url failed the URL checks
400{"code":"no_stripe_account","error":"Connect Stripe 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