Softlemon Merchant API
Production-ready merchant integration endpoints for admin-provisioned API keys.
Base URL: https://api.sandbox.softlemons.com/
This documentation covers the merchant integration surface for admin-provisioned API keys.
Use these endpoints when a merchant is processing payments for its own account.
API keys are created by your SoftLemon admin team and delivered securely. Self-service onboarding and API key generation are not currently available.
The gateway also sends signed outbound webhooks on transaction state changes. Register your endpoint, rotate its secret and replay deliveries through the Webhook Management endpoints. See the Webhooks section below and the webhook integration guide for signature verification examples and retry semantics.
Authenticating requests
To authenticate requests, include an Authorization header with the value Bearer {YOUR_API_KEY}. Use the API key provisioned by your SoftLemon admin team.
Key scopes
Every key carries a list of scopes that limits what it may do. The scopes are transactions:read (transaction status, payment sessions and stored cards), transactions:write (sales, authorizations, captures, voids, 3D Secure verification, payment sessions and stored-card removal), refunds:write (refunds) and webhooks:manage (webhook endpoint, secret rotation, test pings and delivery history). A key is issued with the full set unless a subset is requested when it is created, so a reporting integration can be given a transactions:read key that cannot move money or change webhook settings. A request outside the key's scopes is refused with HTTP 403 and code ERR_AUTH_FAILED, and the message names the missing scope. GET /api/v1/key-info returns the scopes of the key you are calling with.
Idempotent retries
Network failures can leave you unsure whether a capture, refund or void went through. Send an Idempotency-Key header (any unique string up to 255 characters, a UUID works well) on POST /api/v1/transactions/{id}/capture, /refund or /void to make retrying safe.
- Repeating the request with the same key and body within 24 hours returns the stored original response with an
Idempotency-Replayed: trueheader. The money moves once. - Reusing a key with a different body returns HTTP 409 with code
ERR_IDEMPOTENCY_CONFLICT. Use a fresh key for each new request. - While the original request is still processing, a retry returns HTTP 409 with code
ERR_IDEMPOTENT_REQUEST_IN_PROGRESS. Wait a moment and send it again with the same key.
A stored decline replays as well, so attempting a declined capture or refund again needs a new key. On POST /api/v1/transactions use the reference field instead. The duplicate protection guide explains how the two mechanisms differ.
3D Secure Integration Flow
SoftLemon uses a server-managed 3DS flow. You do not need to handle CAVV, ECI or DS Transaction IDs yourself.
A verification is identified by its public_id (format tds_...). That is the value to store and to send back as card_verification_data.id. The numeric id also present in the verify response is deprecated: it is kept for existing integrations and will be removed from the response on a date announced in advance (numeric ids you have already stored remain accepted on input). Treat ids as opaque strings and ignore response fields you do not recognise; new fields are added over time.
Step 1: Initiate 3DS Verification
POST /api/v1/3ds/verify with card details, amount, currency and auth_url (your frontend return URL).
The card object is the same one POST /api/v1/transactions takes. exp_month is 1 to 12 as an integer or a string, with or without a leading zero, so 1, "1" and "01" all mean January. exp_year is the four digit year as an integer or a string.
Step 2: Handle the response
- Frictionless: The response returns immediately with the outcome in
status(see the table below) and amessagethat spells it out. Use the returnedpublic_idin Step 4. - Challenge: The response includes a
challenge_urlandstatus: challenge_pending. Redirect the cardholder to this URL to complete the bank challenge.
Step 3: Cardholder completes challenge (if applicable)
After the cardholder completes the challenge, they are redirected to your auth_url with query parameters:
?card_verification_id={public_id}&status={outcome}
card_verification_id is the verification's tds_... public id, a string. Pass it through unchanged in Step 4.
Verification outcomes
status | Meaning | What to do |
|---|---|---|
full_auth | The issuer authenticated the cardholder. | Proceed to Step 4. Liability shift applies. |
attempt | The issuer could not fully authenticate the cardholder but returned an attempt proof (ECI 06/01). | Proceed to Step 4. The attempt proof is forwarded with the payment. |
unavailable | The card is not enrolled or authentication could not be performed (issuer or 3DS service unavailable). No authentication data exists. | You may still create the payment. It is processed without 3DS and may be soft-declined with ERR_3DS_REQUIRED where SCA is mandatory. |
failed | The issuer refused the authentication. | Do not proceed. Ask for another card or run a new verification. An immediate failure on POST /api/v1/3ds/verify is returned as HTTP 400 with code ERR_3DS_FAILED. |
challenge_pending | The cardholder is still with the issuer. | Wait for the redirect to your auth_url. |
Step 4: Initiate the transaction
POST /api/v1/transactions with card_verification_data.id set to the verification's public_id from Step 2 or the card_verification_id from the redirect in Step 3.
JSON
The API automatically retrieves the stored 3DS authentication data (including CAVV and ECI) and passes it to the payment gateway. A payment that references a failed or unfinished verification is refused with HTTP 422 (ERR_3DS_FAILED or ERR_3DS_REQUIRED).
Sending a payment without a verification
POST /api/v1/transactions never starts a challenge and never returns a redirect. If you send a sale or authorization without card_verification_data and the issuer requires authentication, the acquirer refuses it: the transaction is recorded as failed with status_reason 3ds_required, and the response is HTTP 400 with code ERR_3DS_REQUIRED, the refused transaction in data.transaction and data.next_action pointing at POST /api/v1/3ds/verify. Run the verification (Steps 1 to 3), then create a new transaction with card_verification_data.id. The refused transaction cannot be continued.
Integration guides
Step-by-step walkthroughs for the most common flows, each with full requests and responses:
- Accept a card payment: 3D Secure verification, the sale and confirmation. Start here.
- Authorize now, capture later: holds, full and partial captures and voids.
- Refund a payment: full and partial refunds and the events they emit.
- Charge a returning customer: repeat payments with a vault token.
- Accept an alternative payment: hosted redirect payments such as Paysafecard through payment sessions.
- Set up webhooks: register your endpoint, capture the signing secret, test and monitor deliveries.
- Webhook integration: signatures, retries, replays and keeping your system in sync.
Prefer exploring in Postman? Download the Merchant API Postman collection and set your API key as the Bearer token on the collection.
3D Secure test cards
Use these sandbox cards when testing POST /api/v1/3ds/verify.
| Scheme | Flow | Returns method data | Card number | Expected result |
|---|---|---|---|---|
| Visa | Frictionless | Y | 4200000000000091 | Successful frictionless authentication. |
| Visa | Frictionless | Y | 4200000000000109 | Attempted authentication. |
| Visa | Frictionless | N | 4200000000000026 | Successful frictionless authentication. |
| Visa | Frictionless | N | 4200000000000059 | Attempted authentication. |
| Visa | Error | N | 4012001037461114 | Technical error. |
| Visa | Error | N | 4012001037141112 | User not enrolled. |
| Visa | Not Applicable | N/A | 4532497088771651 | Card not participating. |
| Visa | Challenge | Y | 4200000000000042 | Challenge flow. |
| Visa | Challenge | Y | 4200000000000067 | Challenge flow. |
| Visa | Challenge | N | 4200000000000018 | Challenge flow. |
| Visa | Challenge | N | 4200000000000075 | Challenge flow. |
| Mastercard | Frictionless | Y | 5200000000000007 | Successful frictionless authentication. |
| Mastercard | Frictionless | Y | 5200000000000023 | Attempted authentication. |
| Mastercard | Frictionless | N | 5200000000000056 | Successful frictionless authentication. |
| Mastercard | Frictionless | N | 5200000000000106 | Attempted authentication. |
| Mastercard | Error | N | 5434580000000006 | Technical error. |
| Mastercard | Error | N | 5457350076543210 | User not enrolled. |
| Mastercard | Not Applicable | N/A | 5497260847316287 | Card not participating. |
| Mastercard | Challenge | Y | 5200000000000015 | Challenge flow. |
| Mastercard | Challenge | Y | 5200000000000049 | Challenge flow. |
| Mastercard | Challenge | N | 5200000000000064 | Challenge flow. |
| Mastercard | Challenge | N | 5200000000000072 | Challenge flow. |
Transaction authorized
Transaction captured
Transaction succeeded
Transaction settled
Transaction partially settled
Transaction paid
Transaction voided
Transaction refunded (fully)
Transaction partially refunded
Transaction failed
Transaction cancelled
Transaction chargeback
Transaction pending (opt-in)
Test ping