Payments API

Get paid by your customers from any page. Create a Stripe-hosted Checkout session from your server, manage reusable payment links, or drop a zero-code pay button on your site. Money settles directly into your own connected Stripe account - SoftSolz takes a 0% platform fee.

Prerequisite - connect Stripe first. Connect Stripe Connect Express in the SoftSolz app (Payments → Connect Stripe) and finish onboarding so charges are enabled. Until then the payment endpoints return 409.

Scopes

All Payments scopes follow the service.payments.* pattern. Declare them on the sk_* key you create in the app's Developers tab.

ScopeAllows
service.payments.checkout.createCreate a hosted Checkout session for a one-off payment.
service.payments.links.readList the payment links in your workspace.
service.payments.links.writeCreate and update payment links.

Server API

Server-to-server endpoints live under /api/v1/services/payments/. Requests with a browser Origin header are rejected by design - call these from your backend. A sk_test_* key routes to your paired sandbox tenant. Pass an optional Idempotency-Key header for replay-safe retries.

Create a Checkout session

POST /api/v1/services/payments/checkout-sessions · scope service.payments.checkout.create

Use this when your backend decides the amount at request time, for example a cart total or a custom quote. You create the session, redirect the buyer to Stripe-hosted Checkout, and the buyer returns to your site afterwards.

Request body

FieldRequiredNotes
amountYesNumber, major currency units (e.g. 49.99).
currencyYes3-letter ISO code (e.g. "gbp").
descriptionNoString shown to the buyer on Checkout.
referenceNoYour order id - echoed back on the webhook.
customer_emailNoPre-fills the buyer's email.
success_urlYeshttp/https URL the buyer returns to on success.
cancel_urlYeshttp/https URL the buyer returns to on cancel.
metadataNoFlat string to string map, up to 40 keys.

Request

POST /api/v1/services/payments/checkout-sessions HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Content-Type: application/json
Idempotency-Key: 11111111-2222-3333-4444-555555555555

{
  "amount": 49.99,
  "currency": "gbp",
  "description": "Order #1042",
  "reference": "1042",
  "customer_email": "buyer@example.com",
  "success_url": "https://yoursite.com/pay/success",
  "cancel_url": "https://yoursite.com/pay/cancel",
  "metadata": { "order_id": "1042" }
}

Response 201

{
  "data": {
    "id": 8123,
    "url": "https://checkout.stripe.com/c/pay/cs_live_…",
    "stripe_session_id": "cs_live_…",
    "amount_cents": 4999,
    "currency": "GBP",
    "status": "created",
    "expires_at": "2026-05-19T18:30:00Z",
    "created_at": "2026-05-19T18:00:00Z"
  }
}

Redirect the buyer's browser to data.url. Stripe collects the card and settles the funds directly into your connected account.

Errors

StatusCodeWhen
409CONNECT_ACCOUNT_MISSINGNo Stripe Connect account created yet.
409CONNECT_ACCOUNT_NOT_READYStripe onboarding incomplete - charges not enabled.
400invalid_amountAmount missing or not a positive number.
400invalid_currencyCurrency not a 3-letter ISO code.
400invalid_urlsuccess_url / cancel_url not http/https.
403insufficient_scopeKey missing service.payments.checkout.create, or a browser origin was sent.
409idempotency_key_conflictSame Idempotency-Key replayed with a different body.

Request body fields:

FieldTypeRequiredDescription
success_urlstring (uri)requiredhttp(s) URL Stripe redirects to after a successful payment.
cancel_urlstring (uri)requiredhttp(s) URL Stripe redirects to if the customer cancels.
payment_link_tokenstring | nulloptionalToken of an existing payment button to bill against. When supplied, currency (and a fixed amount, if the button has one) come from the button. Omit to charge an ad-hoc amount (then amount_cents and currency are required).
amount_centsintegerconditionalAmount to charge in MINOR units (cents/pence). Required for ad-hoc charges and custom-amount buttons; ignored when the button has a fixed amount. Minimum 1.
currencystringconditionalISO 4217 3-letter currency code. Required for ad-hoc charges; taken from the button when payment_link_token is supplied.
descriptionstring | nulloptionalLine-item name on the Stripe Checkout page (max 250 chars).
referencestring | nulloptionalFree-text reference stored in Stripe metadata (max 500 chars).
customer_emailstring (email) | nulloptionalPre-fills the customer email on the Checkout page.

Response fields (inside data):

FieldTypeDescription
urlstring (uri) | nullHosted Stripe Checkout URL to redirect the customer to.
stripe_session_idstringStripe Checkout Session id.
amount_centsintegerCharged amount in minor units (resolved from the button or the request).
currencystringISO 4217 currency code, upper-cased.
payment_link_idinteger | nullId of the payment button used, or null for an ad-hoc charge.
expires_atstring (date-time) | nullWhen the Stripe Checkout session expires.

List payment links

GET /api/v1/services/payments/payment-links · scope service.payments.links.read

Returns the payment links in your workspace, newest first. Supports limit and offset query parameters; the response carries meta.has_more so you can stop paging without an extra round-trip.

Request

GET /api/v1/services/payments/payment-links?limit=50&offset=0 HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…

Response 200

{
  "data": [
    {
      "id": "lnk_8f3c2a1b9d",
      "title": "Studio deposit",
      "description": "50% non-refundable booking deposit",
      "amount_cents": 5000,
      "min_amount_cents": null,
      "currency": "GBP",
      "amount_editable": false,
      "active": true,
      "url": "https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d",
      "created_at": "2026-05-12T09:14:00Z"
    }
  ],
  "meta": { "limit": 50, "offset": 0, "has_more": false }
}

For a "pay what you want" link, amount_cents is null, amount_editable is true, and min_amount_cents may carry an optional floor.

Create a payment link

POST /api/v1/services/payments/payment-links · scope service.payments.links.write

A payment link is a reusable, shareable page hosted by SoftSolz. Once created, share data.url anywhere, or drop the zero-code button on your site.

Request body

FieldRequiredNotes
titleYesShown to the buyer on the hosted page.
descriptionNoOptional supporting text.
currencyYes3-letter ISO code (e.g. "gbp").
amountConditionalFixed price in major units. Omit when amount_editable is true.
amount_editableNotrue lets the buyer enter the amount. Defaults to false.
min_amountNoOptional floor in major units when amount_editable is true.
show_business_nameNotrue shows your workspace name on the button and hosted page. Defaults to false.
button_colorNoHex colour for the Pay button background (e.g. "#1d4ed8"). Defaults to a dark button.
button_text_colorNoHex colour for the Pay button text (e.g. "#ffffff"). Defaults to white.

Request

POST /api/v1/services/payments/payment-links HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Content-Type: application/json
Idempotency-Key: 22222222-3333-4444-5555-666666666666

{
  "title": "Studio deposit",
  "description": "50% non-refundable booking deposit",
  "currency": "gbp",
  "amount": 50.00,
  "amount_editable": false
}

Response 201

{
  "data": {
    "id": "lnk_8f3c2a1b9d",
    "title": "Studio deposit",
    "description": "50% non-refundable booking deposit",
    "amount_cents": 5000,
    "min_amount_cents": null,
    "currency": "GBP",
    "amount_editable": false,
    "active": true,
    "url": "https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d",
    "created_at": "2026-05-19T18:00:00Z"
  }
}

Request body fields. Amounts are preferably given in MINOR units (cents/pence) via amount_cents / min_amount_cents. The legacy amount / minAmount fields in MAJOR units (pounds/dollars) still work but are deprecated aliases - prefer the *_cents fields.

FieldTypeRequiredDescription
titlestringrequiredButton title / Stripe line-item name (min length 1).
currencystringrequiredISO 4217 3-letter currency code. Stored upper-cased.
kindstringoptionalSurface type, one of button or link. Any value other than "link" is treated as "button".
amount_centsinteger | nulloptionalFixed charge in MINOR units (cents/pence) - the preferred, API-consistent field. When set, the button is fixed-amount and the minimum is ignored.
min_amount_centsinteger | nulloptionalMinimum allowed amount in MINOR units for a custom-amount button (used only when amount_cents is omitted).
amountnumber | nulloptional (deprecated)Deprecated legacy alias for amount_cents but in MAJOR units (e.g. pounds/dollars); used only when amount_cents is absent. Prefer amount_cents.
minAmountnumber | nulloptional (deprecated)Deprecated legacy alias for min_amount_cents in MAJOR units (also accepted as min_amount); used only when the *_cents fields are absent.
descriptionstring | nulloptionalOptional longer description (max 2000 chars).
showBusinessNamebooleanoptionalShow the tenant business name on the hosted page. Also accepted as show_business_name.
buttonColorstring | nulloptionalHex button background colour. Also accepted as button_color.
buttonTextColorstring | nulloptionalHex button text colour. Also accepted as button_text_color.

Update a payment link

PATCH /api/v1/services/payments/payment-links/:id · scope service.payments.links.write

Change the title, description, amount, or activation state of an existing link. Send only the fields you want to change. Set active to false to stop the link taking new payments without deleting it.

Request

PATCH /api/v1/services/payments/payment-links/lnk_8f3c2a1b9d HTTP/1.1
Host: app.softsolz.uk
Authorization: Bearer sk_live_…
Content-Type: application/json

{
  "title": "Studio deposit (spring season)",
  "amount": 75.00,
  "active": true
}

Response 200

{
  "data": {
    "id": "lnk_8f3c2a1b9d",
    "title": "Studio deposit (spring season)",
    "description": "50% non-refundable booking deposit",
    "amount_cents": 7500,
    "min_amount_cents": null,
    "currency": "GBP",
    "amount_editable": false,
    "active": true,
    "url": "https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d",
    "updated_at": "2026-05-19T18:05:00Z"
  }
}

Errors

StatusCodeWhen
400validation_failedBody fails validation (e.g. amount on an editable link).
403insufficient_scopeKey missing the required scope, or a browser origin was sent.
404not_foundNo payment link with that id in your workspace.

Pay button & widget

Two ways to take a payment from a public page with no server of your own. Both point at a payment link you created with the Server API or in the SoftSolz app.

Zero-code button

The simplest option. A payment link's url opens the SoftSolz-hosted pay page. Wrap it in a plain anchor and style it however you like - no script, no key.

<a href="https://app.softsolz.uk/pay/link/lnk_8f3c2a1b9d"
   target="_blank" rel="noopener">Pay now</a>

Paste this anywhere a link works: a website button, an email, an invoice reminder, or behind a QR code.

Embeddable widget

For an in-page checkout that opens without leaving your site, use the shared loader at https://app.softsolz.uk/softsolz.js with data-service-id="payments".

<script src="https://app.softsolz.uk/softsolz.js"
  data-service-id="payments"
  data-public-key="pk_live_…"
  data-token="lnk_8f3c2a1b9d"></script>
AttributeRequiredNotes
data-service-idYesAlways payments for this widget.
data-public-keyYesA pk_test_* / pk_live_* key with a domain allowlist.
data-tokenYesThe payment link token the widget should check out.

The pk_* public key carries a domain allowlist - a request from an origin not on the list is rejected and the widget refuses to load. See Embeds for the loader, modes, and the allowlist rules.

Webhook events

EventWhen
payments.payment.succeededA buyer completes a Checkout session or pays a payment link.

payments.payment.succeeded data payload:

{
  "payment_id": 8123,
  "amount_cents": 4999,
  "currency": "GBP",
  "reference": "1042",
  "customer_email": "buyer@example.com",
  "stripe_session_id": "cs_live_…",
  "stripe_payment_intent_id": "pi_…",
  "payment_link_id": "lnk_8f3c2a1b9d"
}

payment_link_id is present only when the payment came from a payment link; it is null for server-created Checkout sessions. The event is delivered to your registered webhook endpoints. See Webhooks for signature verification and retries.