Docs

Docs / Guides / Platforms and partners

Platforms and partners

A point-of-sale system, a property manager, a booking engine: your product has customers of its own, and each of them signs waivers or agreements. This page is how that works, and who pays whom.

How your customers get credentials

Every business you serve is a business in SignSealer — its own templates, documents, evidence and team, isolated from every other by SignSealer, not by anything your code has to get right. Our servers connect to the database as the owner of its tables, so the isolation they keep is in the functions they call: each checks which business the caller belongs to and filters what it reads and writes by that business. Row level security on every table binds anybody who reaches the tables directly through the data API. Today each one connects the same way any customer does: somebody at that business signs in to the portal, opens Integrations → API keys, and creates an API key with the scopes your integration needs (signing:read and signing:write for most). Your product stores that key for that business and sends it as Authorization: Bearer. Keys are scoped, revocable, and shown once; the authentication page has the details.

Or your product asks the business once. Send the person to /oauth/authorize, they agree on a consent screen that shows your registered name and the scopes you asked for, and your server exchanges the code at /oauth/token for an access token that works for an hour and a refresh token that gets the next one. It is standard OAuth 2.0 with PKCE; any client library speaks it, and the authentication page has the two requests in full. The business can end the connection under Integrations → Connected apps whenever it likes. Nothing about the key-based path changes.

We handle the signing; you share in the revenue

The businesses you bring pay SignSealer directly, and we run everything for them — billing, support, the signing pages, the texts, the evidence. You share in what they pay, month after month, for as long as they stay. The terms are in your partner agreement; tell us about your product and we will send you yours.

  • Which businesses are yours. One that signs up through your link, installs your app in its first weeks, or redeems a promotion code made for you. Once it is yours it stays yours. A business your own people own — somebody on your partner dashboard, or one of your addresses — is not one you bring, and is not counted.
  • When your share starts. A person here looks at every new partner account first. Your link works from the day you enroll and businesses that sign up through it are recorded as yours; your share starts on the day we approve the account, on what they pay from then on.
  • When you are paid. Each month closes into a statement that does not change afterwards: each business, what it added, and what you are owed.
  • Seeing it yourself. Your partner dashboard shows your link, your businesses and every statement, and the same figures are in the API at /v1/partner, read with your OAuth client's credentials — never a business's key.

Custom branding

The businesses you bring can sign on pages wearing your name, colors and logo, with Powered by SignSealer at the foot, and their mail says it was sent through you. You set it up yourself from your partner dashboard, at no charge. Each business that texts does so from a number of its own, registered in its own name (coming soon). A business that buys white label for itself shows its own brand instead.

Texting numbers for the businesses you serve

Each business that texts does so from a number of its own, applied for by its owner or an admin under the business's legal name: the carriers register the business, not you, so you cannot apply for it. What you can do is take each owner to the right page with your answers already in it, and show where each one has got to on your own screen.

  • Which businesses need one. GET /v1/account with each business's key or token: texting.why_not is number while it waits for its own, and texting.application says where its application has got to (status, phone once verified, reason when the carriers asked for a change), or null when it has not applied.
  • A link for its owner. POST /v1/texting/number/invitations with what you know, in the application's own names (legal_name, ein, website, the address, the contact, the kind of number). Put the url it returns in front of the owner. It opens, after they sign in, on that business, with your answers filled in and your app named as the one that filled them; they read each one, tick that it is true and apply. Fourteen days, used up when they apply.
  • Each step as it happens. Subscribe the business's endpoint to texting.number_updated, or to * (Webhooks).

Billing your customers yourself, by agreement

Where you would rather your customer paid you alone, a business can be billed through a partner: it pays SignSealer nothing, its billing page says who bills it, and its completed agreements roll up onto your monthly statement at a wholesale rate agreed with us. It is set up by hand, for the deal that needs it.

Either way, start with the partner form: a few questions about your product and your customers, and a quote comes back to you by email.

What stays the same

  • Isolation. A key for one business reaches that business and no other, whoever holds it.
  • Evidence. The certificate, the trail and the sealed PDF belong to the business that sent the document, and it can download them without you.
  • Webhooks. Register your endpoint per business, or one endpoint that reads tenant_id from the payload; either way the signature is per business.
  • The signer's experience. The signing page is served by us, in our name or with your custom branding over it; embed the link, text it, or open it on a tablet. It loads nothing from anywhere else.

The partner API

Three routes, and all three only read: your program and this month so far, the businesses that are yours, and every month that has closed. They answer on https://api.signsealer.com, beside the rest of the API. Nothing you send changes what you earn; the share and the program are set by SignSealer.

Credentials

Send your OAuth client's client_id (ss_client_…) and client_secret (ss_secret_…) by HTTP Basic, the same pair /oauth/token takes.

curl -u "$SIGNSEALER_CLIENT_ID:$SIGNSEALER_CLIENT_SECRET" https://api.signsealer.com/v1/partner

Your client id is on your partner dashboard, under API and app installs. The secret is shown once and kept only as a hash. A partner account set up from its quote is not sent one, so make it there: the dashboard's owner presses Make a new client secret, and only the owner can.

A new secret replaces the old one at once. It is the same secret your app sends to /oauth/token, so give your app the new one at the same time: until it has it, exchanging a code or refreshing a token there fails.

The credentials work from the day you enroll, while your account is still under review. A business's API key or access token is refused, and so are a public client (one with no secret), a disabled client and a client that is not enrolled as a partner. Each of them gets the same 401 unauthorised with a WWW-Authenticate: Basic header: which part was wrong is not the caller's to learn.

GET /v1/partner

Your program, this month so far, and the link to hand a business. The figures in these examples are made up; your share is the one on your quote.

{
  "partner": "DeckPOS",
  "client_id": "ss_client_0123456789abcdef0123456789abcdef",
  "referral_code": "deckpos",
  "share_percent": 10,
  "active": true,
  "custom_branding": true,
  "review": "approved",
  "earning": true,
  "earning_since": "2026-09-02T15:04:05+00:00",
  "businesses": 12,
  "month_to_date": {
    "month": "2026-10-01",
    "businesses": 9,
    "gross_cents": 43900,
    "fees_cents": 1650,
    "sms_cost_cents": 1000,
    "revenue_cents": 41250,
    "share_bp": 1000,
    "share_cents": 4125
  },
  "signup_url": "https://app.signsealer.com/signup?partner=deckpos"
}
FieldMeans
partnerThe name your OAuth client is registered under.
referral_codeThe code on the end of your signup link.
signup_urlYour signup link: https://app.signsealer.com/signup?partner= and your code.
share_percentYour share, as a percentage: 10 is 10%.
activefalse while your program is paused. No new business is made yours; the ones you have stay yours and keep earning.
custom_brandingWhether your custom branding is on.
reviewpending, approved or declined. pending until a person here has looked at your account.
earningtrue once your account is approved.
earning_sinceWhen your share started counting; null until you are approved.
businessesHow many businesses are yours, closed ones included.
month_to_dateThis calendar month so far, in UTC, worked out the way its statement will be (below). month is its first day; businesses counts the ones that added something; share_bp is your share in hundredths of a percent; share_cents is truncated to the cent.

Nothing accrues before your account is approved, so until then every amount in month_to_date is 0. Businesses that sign up through your link meanwhile are still recorded as yours.

GET /v1/partner/businesses

The businesses that are yours, newest first by when they became yours.

QueryMeans
limitA whole number from 1 to 200; 50 when left out.
offsetHow many to skip: 0 or more; 0 when left out.
{
  "data": [
    {
      "tenant_id": "7d9c1c1e-7a57-4f55-9a86-0f7f3f9c2b11",
      "name": "Harbour Rentals",
      "status": "active",
      "source": "referral_link",
      "attributed_at": "2026-08-02T15:04:05+00:00",
      "month_to_date_cents": 1840
    }
  ],
  "has_more": false
}

There is no total. Ask again with offset plus limit until has_more is false. A business that becomes yours while you walk the list appears at the front and moves the rest along by one.

  • status is active, suspended or closed.
  • source is how it became yours: referral_link (it signed up through your link), oauth_install (it installed your app in its first thirty days), promotion (it redeemed a promotion code made for you) or ops (SignSealer set it).
  • month_to_date_cents is what it has added this month after card fees and the carrier's cost of its texts. It counts from the latest of the start of the month, when the business became yours and when your account was approved, and it is 0 until you are approved. Credit SignSealer gave away earns nothing.

GET /v1/partner/statements

Every closed month, newest first, under data. The list is not paged. A month closes on the first run of our sweep after midnight UTC on the 1st, and only for an approved account, so there are none while yours is under review. A statement is written once: afterwards only paid and paid_at change, when SignSealer records the payment.

FieldMeans
monthYYYY-MM.
businessesHow many businesses added something that month: one entry each in lines.
gross_centsWhat they paid for and used.
fees_centsThe card fees on that, in proportion.
sms_cost_centsThe carrier's cost of their texts, in proportion.
revenue_centsgross_cents less the fees and the texts' cost: what your share is of.
share_percentYour share at the rate in force when the month closed.
share_centsThat share of revenue_cents, truncated to the cent once, on the month's total.
carried_in_centsA shortfall from your previous statement: 0 or negative.
payable_centsWhat you are owed for the month: 0 or more.
carried_out_centsA shortfall carried into the next month: 0 or negative.
paid, paid_atWhether SignSealer has recorded the payment, and when.
linesOne per business that added something: tenant_id, name, gross_cents, fees_cents, sms_cost_cents and revenue_cents. A refund can make a line negative, and it is kept.

The sums always meet: payable_cents plus carried_out_cents is share_cents plus carried_in_cents. A month that comes to less than nothing is carried into the next rather than invoiced.

Errors

The body is the API's usual { "error": { "code": …, "message": … } }, and every answer says Cache-Control: no-store.

StatusCodeWhen
401unauthorisedNo Basic credentials, or ones that do not open a partner account.
400invalid_limitlimit on /v1/partner/businesses is not a whole number from 1 to 200.
400invalid_offsetoffset is not a whole number, 0 or more.
404not_foundA path under /v1/partner that is not one of the three.
405method_not_allowedAnything but GET (or HEAD), with Allow: GET.

From the Node client

SignSealerPartner in @signsealer/sdk, from version 1.8.0, which reached npm in 1.10.0. It takes your client id and secret and refuses a business's API key (anything starting mk_) when it is built. Every call only reads, so a network failure, a 429 or a 5xx is tried again as it is, up to maxAttempts (3, the first included); anything else throws a SignSealerError at once. Keep the secret on a server.

import { SignSealerPartner } from "@signsealer/sdk";

const partner = new SignSealerPartner({
  clientId: process.env.SIGNSEALER_CLIENT_ID!,
  clientSecret: process.env.SIGNSEALER_CLIENT_SECRET!,
});

const me = await partner.profile();              // GET /v1/partner
if (!me.earning) console.log("under review:", me.review);

for (let offset = 0; ; offset += 200) {          // GET /v1/partner/businesses
  const page = await partner.businesses({ limit: 200, offset });
  for (const b of page.data) console.log(b.name, b.source, b.month_to_date_cents);
  if (!page.has_more) break;
}

const months = await partner.statements();       // GET /v1/partner/statements: the data array

SignSealerPartner.signupUrl(me.referral_code);   // me.signup_url, built without a call

In the OpenAPI description

The three routes are in /openapi.json under the Partners tag, with a security scheme of their own, partnerAuth (HTTP Basic), in place of the API key the document declares by default. The API reference draws them from the same declarations.

Ready to build? An API key takes a minute in the portal, and the free plan covers the first 10 agreements a month.

Get an API key