Create a payment server-side, then send the donor through Repejo's hosted
checkout (redirect) or an embedded
<repejo-checkout>
element, and receive the outcome via webhooks. This is Repejo's "Pay by Link" capability.
The donor completes the recurring mandate (e.g. Autogiro)
with BankID on their own device.
The OpenAPI document is the source of truth — it contains every operation, schema, enum and error. Coding agents: point your tool at the OpenAPI URL.
All requests use a Bearer API token scoped to a single organisation. An organisation admin mints one in the back office under Settings → API-nycklar; the secret is shown once.
Authorization: Bearer <api_token>
A missing or invalid token returns 401:
{ "errors": { "detail": "Unauthorized" } }
An api
checkout is locked to a single payment period. To offer both one-time
and monthly
donations, create two separate checkouts
— one per periodicity — each with
its own checkout_id
(chk_…), and
pick the one that matches the donation type when creating the session.
A payment_method
whose periodicity doesn't match the checkout is rejected with 422 payment_method_not_valid_for_period.
api
("API") type checkout per periodicity
and note each checkout_id
(shown on the checkout's Användning
tab).
payment_url, or embed the checkout.
payment_session.*
webhooks.
curl -X POST https://app.repejo.se/api/v1/payment_session \
-H "Authorization: Bearer <api_token>" \
-H "Content-Type: application/json" \
-d '{
"amount": 200,
"currency": "SEK",
"checkout_id": "chk_123412",
"payment_method": "autogiro",
"metadata": { "lead_id": "12345" },
"success_url": "https://example.org/success",
"cancel_url": "https://example.org/cancel"
}'
Response 201:
{
"payment_url": "https://app.repejo.se/s/aunt123?rp_token=abc123",
"session_token": "abc123",
"short_code": "aunt123"
}
amount
(number, required) — the donation amount, > 0, in the checkout's
currency. The checkout's periodicity decides how it is charged: on a
recurring checkout it is the per-period amount (e.g. monthly), confirmed
by payment_session.succeeded
(mandate signed); on a one-time checkout it is the single charge.
currency
(required) — one of SEK, EUR.
Must be a currency the checkout offers.
checkout_id
(string, required) — must be an api-type checkout.
payment_method
(optional) — preselects and opens that method; one of autogiro, swish, swish_onetime, card, apple_pay, google_pay, sepa_direct_debit. Must be enabled on the checkout. Omit to let the donor choose.
payer
(optional object) — first_name, last_name, phone_number, email, all optional.
metadata
(optional object) — echoed on every webhook (e.g. lead_id).
success_url
/ cancel_url
(required) — absolute http/https
URLs the donor returns to.
400
— malformed body, or currency/payment_method
outside the enum.
401 — missing/invalid token.
422 {"error":"checkout_not_found"}
— the checkout_id
is unknown or malformed (e.g. missing the
chk_
prefix).
422 {"error":"checkout_not_api"}
— the checkout isn't an api
checkout.
422 {"error":"currency_not_enabled"}
— the currency
isn't one the checkout offers (e.g.
EUR
on a SEK-only checkout).
422 {"error":"payment_method_not_enabled"}
— that method isn't enabled on the checkout.
422 {"error":"payment_method_not_valid_for_period"}
— that method's periodicity doesn't match the checkout (e.g.
swish_onetime
on a recurring checkout, or
autogiro
on a one-time checkout).
422 {"error":"invalid_success_url"}
/
422 {"error":"invalid_cancel_url"}
— the URL isn't an absolute http/https
URL. Non-http(s) schemes
(javascript:, data:),
relative paths and empty strings are rejected.
Redirect the donor's browser to payment_url. The hosted
checkout skips the amount screen and opens the (optionally preselected) payment method. On
completion Repejo redirects to your success_url; the back button
cancels and redirects to your cancel_url.
Render the custom element on your own page (load
checkout.js
once), passing the short_code
and session_token
from the response.
<script defer src="https://app.repejo.se/assets/checkout.js" data-host="https://app.repejo.se"></script>
<repejo-checkout short_code="aunt123" session-token="abc123" host="https://app.repejo.se"></repejo-checkout>
In embedded mode the element dispatches DOM events instead of redirecting. They bubble from the <repejo-checkout>
element up to window, so listen on
either. Both recurring (mandate signed) and one-time (payment captured) completions fire repejo:completed;
event.detail
carries type
("recurring"
| "onetime").
window.addEventListener("repejo:completed", (e) => { /* e.detail.type → show your thank-you */ });
window.addEventListener("repejo:cancelled", (e) => { /* donor cancelled */ });
The element also emits informational events you can ignore:
repejo:first-interaction
(donor started) and
repejo:registered
(left the amount step). Only
repejo:completed
/ repejo:cancelled
are part of the contract.
Treat the
payment_session.succeeded
webhook as the authoritative signal; the DOM events are a UX convenience.
Outcomes are delivered to the tenant webhook endpoints you register in the back office
(subscribe them to the
payment_session.*
events). Delivery is asynchronous, signed and retried.
type PaymentSessionEvent =
| "payment_session.created" // session created (donor not yet finished)
| "payment_session.succeeded" // recurring mandate signed OR one-time payment captured — mark the lead done
| "payment_session.cancelled"; // donor cancelled
payment_session.succeeded
is the single reconciliation signal for both
periodicities: a recurring checkout fires it when the mandate is signed, a one-time
checkout when the single payment (Swish / card) is captured. You do not need to consume
transaction.*
to confirm a one-time gift.
Each delivery has this envelope;
data.metadata
carries your lead_id:
{
"sent_at": "2026-06-13T12:00:00Z",
"event_type": "payment_session.succeeded",
"data": {
"id": "pls_8x…",
"checkout_id": "chk_123412",
"status": "succeeded",
"amount": "200",
"currency": "SEK",
"payment_method": "autogiro",
"metadata": { "lead_id": "12345" }
}
}
See the
Webhooks reference
for the full signing (Repejo-Signature: sha256=<hex>),
retry and idempotency contract.