Skip to main content

Team access and roles

Your business is a workspace, and more than one login can be in it. Invite colleagues from Settings in your dashboard, and give each of them a role that decides what they can reach.

The one thing worth deciding before you invite anyone: only Owner and Admin can see your API key and webhook secret. Everyone else can work in the dashboard without ever being shown a credential.

The four roles​

RoleCan do
OwnerEverything, plus transferring ownership. Exactly one per business
AdminEverything the owner can, except transferring ownership
FinanceView, refund, and work with invoices and exports. Cannot change bank details, create or cancel splits, or reach your credentials
MemberDay-to-day operations: view splits, create them, cancel them. Cannot refund, and cannot reach settings or credentials

Every change to bank details, business details, payment processors, the team, API keys and webhook settings is recorded with who made it, their role and when, in a history that cannot be edited or deleted. A change to bank details also emails the owner, naming who made it.

Read across, by what someone actually does:

ActionOwnerAdminFinanceMember
View splits and the webhook delivery log✅✅✅✅
Create a split, cancel a pending split✅✅✅
Refund a captured split✅✅✅
Reveal or rotate the API key and webhook secret✅✅
Set the webhook URL and allowed return domain✅✅
Send a test webhook, resend a delivery✅✅
Start or refresh Stripe Connect onboarding, remove a processor✅✅
Change invoicing and bank details✅✅
Invite, re-role and remove team members✅✅
Transfer ownership✅

A call made without the right role returns 403 {"code":"business_forbidden","error":"You do not have permission to perform this action for this business."}.

Refund and cancel are deliberately split

Finance can refund but cannot cancel; a Member can cancel but cannot refund. Cancel only ever releases holds, so it cannot move money and is safe to leave with the people running orders. Refund returns money that has already settled, so it sits with the people who own the books. Give someone both by making them an Admin.

Inviting someone​

Nobody is added directly. You send an invitation, and they accept it from an email while signed in to their own SharePay account.

POST /api/merchants/team/invites
{ "email": "[email protected]", "role": "finance" }

role must be admin, finance or member. You cannot invite an owner, and you cannot grant a role you do not hold: an Admin may invite Finance and Member, but not another Admin. Trying returns 403 You cannot grant that role.

Returns 201 { id, email, role, expires_at } and emails the invitation. If that email cannot be sent, the invitation is revoked again and you get 502 The invitation email could not be sent. Please try again. rather than a pending invite nobody will ever receive.

ResponseCause
400 Enter a valid email and role.Malformed email, or a role outside the three above
403 You cannot grant that role.Admin trying to create another Admin, or anyone trying to create an Owner
409 That person is already a team member.They are already in this workspace
409 An invitation is already pending for this email.Revoke or resend the existing one instead

Invitations last 7 days. An expired one cannot be accepted; resend it to mint a fresh link.

Managing pending invitations​

POST /api/merchants/team/invites/:id/resend
DELETE /api/merchants/team/invites/:id

Resending replaces the link. It issues a new token and a new 7-day expiry, so any link you sent before stops working. It returns { ok: true, expires_at }, or 502 if the email fails — in which case the previous link is restored and still works.

DELETE revokes the invitation. Both return 404 Pending invitation not found. for an invitation that was already accepted, revoked or never existed.

Invitations are rate limited to 5 requests per 10 minutes, keyed by your IP address, and that bucket is shared with credential rotations and webhook test sends. Inviting a handful of colleagues in one sitting will lock all three out with 429 Too many requests. Please try again later. See Limits.

Accepting an invitation​

The invited person opens the link in their email, which lands on /business-invite/<token>. Two rules decide whether it works:

  • They must be signed in to SharePay with the address the invitation was sent to. A different account gets 403 This invitation was sent to <email>. Sign in with that email to accept it. If they have no SharePay account at all, they sign up with that address first — an invitation does not create a login for them.
  • The link is single-use. A second accept returns 409 This invitation has already been used or cancelled.

Behind that page:

GET /api/merchants/team-invites/:token
POST /api/merchants/team-invites/:token/accept

The GET takes no credentials and shows the invitation before anyone signs in — { business_name, role, email_masked, expires_at }, with the address masked to co***@your-shop.example so a leaked link does not leak an email. An expired or unknown token returns 404 This invitation is invalid or has expired.

The POST needs the invited person's session and returns { ok: true, merchant_id }.

Reading and changing the team​

GET /api/merchants/team

Any member may read it. Returns current_role and current_user_id — which is how the dashboard knows what to show you — alongside members (each with id, user_id, role, email, name, joined_at) and every pending invites row.

PATCH /api/merchants/team/members/:id
DELETE /api/merchants/team/members/:id

PATCH takes { "role": "member" } and returns { id, role }. You must be able to manage both the role they hold now and the role you are moving them to, so an Admin cannot promote a Member to Admin, and cannot re-role another Admin. Either failure returns 403 You cannot change this team member's role.

DELETE removes their access immediately and returns { ok: true }. You cannot remove yourself — that returns 400 You cannot remove your own access here. — and you cannot remove someone whose role you do not manage.

Neither call can touch the Owner. To change who the owner is, transfer ownership.

Transferring ownership​

Owner only, and it is not reversible from your side afterwards.

POST /api/merchants/team/transfer-ownership
{ "member_id": "<their team member id>", "confirmation": "Corner Coffee" }

confirmation must be your business name typed exactly, or the call returns 400 Enter the business name exactly to confirm the transfer. member_id is the id from GET /api/merchants/team, not their user_id, and it must be someone already in the workspace.

You are demoted to Admin, they become Owner. You keep every day-to-day permission, so nothing about running the business changes, but you can no longer transfer ownership, and only they can transfer it back.

Verification starts again for the new owner. Business verification is tied to the owner's identity, so the transfer clears the previous result and re-runs the check straight away:

  • If the new owner has not verified their identity in the SharePay app, the business cannot send invoices or take payments until they do. Their verification is theirs alone; yours does not carry over.
  • If they are verified but Companies House does not list them as a director or officer, the business goes to review and SharePay decides by hand, as it would for a first registration.
  • Stripe, orders, invoices and the team are untouched.

GET /api/merchants/team marks each member with identity_verified so you can see this before you transfer.

ResponseCause
403 Only the owner can transfer ownership.You are not the owner
400 Choose another current team member.member_id is you, or is not in this workspace
409 Ownership changed while you were confirming. Reload and try again.Someone else transferred ownership first

Being in more than one business​

One login can belong to several businesses — your own, plus any you have been invited into.

GET /api/merchants/workspaces

returns { workspaces: [{ merchant_id, name, role }] } for every business the signed-in account can reach.

A second workspace changes every other call

While your login belongs to exactly one business, every endpoint resolves it for you and nothing changes. The moment it belongs to two, the merchant endpoints stop guessing and answer 409 {"code":"business_selection_required","error":"Select a business workspace before continuing."} until you name one.

You name it with a header:

x-sharepay-merchant-id: <merchant_id>

This only works from your server. Browsers are not allowed to send that header to our API, so the dashboard cannot send it for you. Which means: if you accept an invitation into a second business using the same login you own your own business with, expect the dashboard to start showing that 409. Use a separate login for each business unless you are calling the API from your own server.

Registering a new business is also once per login. A second POST /api/merchants/register returns 409 {"code":"merchant_exists","error":"You already have a merchant account"}. Being invited into other workspaces does not change that: you can join many, and create one.

If your workspace is paused​

A workspace SharePay has paused keeps working for reading only. Every action beyond viewing returns 403 {"code":"business_forbidden","error":"This business workspace has been paused. Contact SharePay support."}, whatever your role, including the owner's. Existing splits are unaffected by the pause itself; contact support to have it lifted.