Overview
Conventions and security
Append every documented route directly to the production base URL. Requests and responses use JSON unless otherwise noted.
HTTPS only
Use HTTPS for every production request.
Server-only credentials
Never expose access keys or HMAC secrets in browser or mobile code.
Authoritative status
Treat the authenticated GET endpoint as the source of truth.
Stable identifiers
Generate a unique instructionId for each new payment or refund.
01
Merchant onboarding
Before onboarding, the merchant needs a Swish Handel number from their bank with BjornTech AB (id 9872884581) as technical provider. See Get started for bank-by-bank instructions.
Onboarding links a merchant account to the integration and returns the credentials required for signed API requests.
/v1/onboarding/startStart merchant onboarding/v1/onboarding/confirm?token=...Complete confirmation and redirect/v1/onboarding/exchangeExchange one-time code for credentialsStart onboarding
/v1/onboarding/start| Field | Required | Description |
|---|---|---|
email | Yes | Merchant email address |
merchantAlias | Yes | Merchant Swish alias |
redirectUri or adminReturnUrl | Yes (one) | Browser return URL after confirmation |
state | No | Opaque state protection value |
nonce | No | Opaque unique integration value |
callbackUrl | No | Payment-status callback endpoint |
siteUrl | No | Merchant-site metadata |
integrationVersion | No | Integrating application version |
Confirm and exchange
GET /v1/onboarding/confirm?token=... returns the browser to the stored URL with swish_onboarding_code and state. Validate state server-side, exchange the transient code immediately, and clean it from the URL.
POST /v1/onboarding/exchange
Content-Type: application/json
{ "code": "ex-opaque" }Store the returned base64 HMAC secret and access key securely on the server. Codes are single-use and expire after 15 minutes by default. Unknown, expired and used codes return 404, 410 and 409 respectively.
02
HMAC authentication
Sign every payment, refund, status and cancellation request with HMAC-SHA256. Build exactly seven fields, separated by LF newlines, with no trailing newline.
METHOD
PATH
QUERY
ACCESS_KEY_ID
TIMESTAMP
NONCE
BODY_SHA256_HEXUse the uppercase method, exact transmitted pathname and encoded query substring, and the lowercase SHA-256 digest of the exact UTF-8 body. Hash the empty string for a bodyless request.
| Required header | Value |
|---|---|
X-BjornTech-Access-Key | Assigned accessKeyId |
X-BjornTech-Timestamp | ISO-8601 timestamp with timezone |
X-BjornTech-Nonce | Unique, unpredictable value |
X-BjornTech-Signature | Lowercase hex HMAC-SHA256 signature |
03
Payments
/v1/swish/payment-requests/:instructionIdCreate payment/v1/swish/payment-requests/:instructionIdAuthoritative status/v1/swish/payment-requests/:instructionIdCancel paymentCreate a payment
/v1/swish/payment-requests/:instructionId| Field | Required | Description |
|---|---|---|
payeeAlias | Yes | Merchant Swish number |
amount | Yes | Decimal string, max two decimals |
currency | Yes | Must be SEK |
callbackUrl | No | HTTPS status callback; omit for polling only |
payerAlias | No | Payer mobile number, international format |
payeePaymentReference | No | Merchant payment reference |
message | No | Message shown for the payment |
{
"payeePaymentReference": "ORDER-10001",
"callbackUrl": "https://merchant.example/swish/callback",
"payerAlias": "46712345678",
"payeeAlias": "1233801768",
"amount": "100.00",
"currency": "SEK",
"message": "Order 10001"
}Payment statuses
| Status | Meaning |
|---|---|
CREATED | Created, not complete |
PAID | Payment successful |
DECLINED | Payer declined |
ERROR | Failed or timed out |
CANCELLED | Payment request cancelled |
Cancel a payment
/v1/swish/payment-requests/:instructionIdSend Content-Type: application/json-patch+json and the exact patch below. A payment can only be cancelled before reaching a final state.
[
{ "op": "replace", "path": "/status", "value": "cancelled" }
]04
Refunds
/v1/swish/refunds/:instructionIdCreate refund/v1/swish/refunds/:instructionIdAuthoritative refund statusCreate a refund
| Field | Required | Description |
|---|---|---|
originalPaymentReference | Yes | Reference from the original successful payment |
payerAlias | Yes | Merchant Swish number issuing the refund |
amount | Yes | Decimal string; cannot exceed refundable amount |
currency | Yes | Must be SEK |
callbackUrl | Yes | HTTPS refund-status endpoint |
payerPaymentReference | No | Merchant refund reference |
message | No | Refund message |
{
"originalPaymentReference": "652ED6A2BCDE4BA8AD11D7334E9567B7",
"callbackUrl": "https://merchant.example/swish/refund-callback",
"payerAlias": "1233801768",
"amount": "100.00",
"currency": "SEK",
"payerPaymentReference": "REFUND-10001",
"message": "Refund for order 10001"
}A successful response means Swish accepted the refund for processing. The body may be empty. Retrieve the refund for authoritative status.
Refund statuses
| Status | Meaning |
|---|---|
CREATED | Created, not complete |
DEBITED | Amount withdrawn from merchant |
PAID | Refund successful |
ERROR | Refund failed |
05
Callbacks and status handling
Callbacks are enabled only when an individual request includes callbackUrl. Polling-only is supported by omitting it.
06
Rate limits and retries
~1,000 requests
per source IP over five minutes — approximately 3.3 requests/second average.
On 429 Too Many Requests, read Retry-After, pause the affected source IP for at least that duration, then continue with bounded backoff and jitter. Do not assume the response body is JSON.
07
Errors
Application errors are generally JSON, but upstream and rate-limit responses may be JSON, non-JSON or empty. Use HTTP status as the primary signal and parse bodies defensively.
Implementation rule
Never infer undocumented endpoints, SDKs, statuses, browser handoff URLs, authentication methods, response shapes or guarantees.