When a payer, a subscription, a receivable, a transaction or a payment session is created or changes, Repejo sends an event to your system. The event is a signed JSON POST to each active endpoint that subscribes to it. Repejo counts the delivery as successful when your endpoint answers with HTTP 2xx.
An endpoint is the HTTPS URL in your system that receives the events. Log in to Repejo as an administrator and do these steps:
Each endpoint gets its own secret key. If you have one endpoint in the test environment and one in production, they have different keys.
type EventType =
| "payer.created"
| "payer.updated"
| "subscription.created"
| "subscription.updated"
| "transaction.created"
| "transaction.updated"
| "receivable.created"
| "receivable.updated"
| "payment_session.created"
| "payment_session.succeeded"
| "payment_session.finalized"
| "payment_session.cancelled";
Repejo sends the
payment_session.*
events for
Payment Sessions
("Pay by Link"). The other events describe what happens to the donor, the mandate and each
charge.
One completed Payment Session sends events from both families, in this order:
payment_session.created // session created (donor not yet finished)
payer.updated // Autogiro: mandate registered on the donor
payment_session.succeeded // recurring mandate signed
payer.updated // donor details from the checkout (skipped when unchanged)
subscription.created // the resulting subscription/mandate (data.id = sub_…)
payment_session.finalized // post-processing done; mandate documents available
The payer.*
events depend on the payment method and on what the donor changed, so
don't rely on their position or count. The API creates the payer when
you create the session, so it sends payer.updated, not payer.created.
To match a Payment Session with your own records, use payment_session.succeeded.
It carries the metadata
you sent on POST /payment_session.
Subscribe to the events you need, from one family or from both, and use
X-Repejo-Event-ID
to find duplicates.
If you need the signed mandate PDF or the BankID signature XML, wait for payment_session.finalized.
Repejo sends it once for each succeeded session, when the documents are ready, with
their download urls in data.documents.
type WebhookPayload = {
sent_at: string;
event_type: EventType;
data: Payer | Subscription | Receivable | Transaction | PaymentSession;
};
amount
is a string, for example "150", not a
number, in both
payment_session.*
and subscription.*
payloads.
A payment_session.succeeded
payload contains the resulting subscription,
payment_method
(object)
and payer
under data
(the same shapes the
subscription.*
and payer payloads carry), so you can match the subscription and the payer without another API call.
payment_session.finalized
adds documents.
They are null
for a one-time session, which has no subscription; the
payer
is always present.
See the
Payment Sessions reference
for a full example.
subscription.created.data.payment_method
for an Autogiro mandate contains personal data:
personal_identity_number
(Swedish personnummer), clearing_number,
account_number
and payer_number.
Make sure that the signature is correct before you parse a payload. Store the payloads safely, and give access to the personal data only to the people who need it.
payer
as a nested object (the same fields the payer payload carries), in
addition to payer_id.
The field is null
when no payer is linked or the payer can no longer be loaded (for
example, it was deleted).
payment_method
as a nested object with type-specific fields (see PaymentMethod).
The field is null
when no payment method is linked.
payer
as a nested object (same fields as the subscription payer). The field
is null
when no payer is linked or the payer can no longer be loaded (for
example, it was deleted).
subscription_id, the subscription of the transaction's
receivable, or null
when the receivable has none. A one-time gift can carry one too, so read
type
("recurring"
or "onetime") to tell a monthly
charge from a one-time payment.
receivable.created
(unpaid, or rejected when no Autogiro payment order stands behind it) and, once paid, receivable.updated.
A card, Apple Pay, Google Pay or Swish charge sends
transaction.created
with status pending
when it starts and
transaction.updated
when the outcome is known. An Autogiro charge sends one
transaction.created
with the outcome from the bank's file. Events can reach you out of order,
because a failed delivery is retried later. A transaction never goes back to pending, so ignore a
pending
event for a transaction you already have with another status.
external_transaction_id
and paid_at.
external_transaction_id
holds the external id when the receivable has a successful transaction with
one — Adyen's pspReference
for an Adyen payment — and is
null
otherwise. paid_at
is the ISO 8601 UTC timestamp of the payment (exact for Swish, card, Apple Pay,
Google Pay and invoice; 12:00 Europe/Stockholm on the paid date for Autogiro,
Bankgiro, Stripe subscriptions and offline payments) and
null
for unpaid receivables. See the Receivable
payload for details.
subscription.created
arrives. The signing sends a paid
receivable.created
and a transaction.created
dated the signing day. If your organisation charges later periods itself
(collected_by
is "organisation"), your first
charge is for the second period. Do not charge the first period again.
payment_session.succeeded
embeds the resulting subscription, the signed
payment_method
object (same type-specific fields as the subscription payment method) and the payer, plus payment_channel,
collected_by
and the deprecated payment_method_type,
which replace the scalar preselect payment_method.
For a one-time session (no subscription) all of these except the payer are null; the
payer
is always present.
data.request_type
echoes what the session was created for, on all four
payment_session.*
events: subscription
or payment_method.
A payment_method
session sets up an Autogiro mandate alone, so its subscription,
amount
and currency
are null
while payment_method
and payer
are populated, and its
documents
carry only the signature XML.
payment_session.finalized
has the same shape as
succeeded
plus a documents
object.
It is sent for every succeeded session once post-processing has finished
(for a one-time payment, straight after succeeded),
whether or not any document exists.
payment_session.finalized
carry a documents
object with mandate_pdf_masked_url,
mandate_pdf_unmasked_url
and signature_xml_url.
Each is an authenticated API url (send your Bearer token) or
null
when that document does not exist at the time of the event: PDF urls appear once the
mandate PDF has been rendered (right after signing for Autogiro and SEPA Direct Debit
mandates on a checkout that enables the mandate language, Swedish / German); the XML url
exists only for mandates signed with BankID. See
Payment Sessions
for the endpoints.
Content-Type: application/json
User-Agent: Repejo-Webhooks/1.0
X-Repejo-Delivery-ID: <delivery_id>
X-Repejo-Event-ID: <event_id>
X-Repejo-Event-Type: <event_type>
X-Repejo-Attempt: <attempt>
Repejo-Signature: sha256=<hex>
Repejo-Signature
is sha256=<hex>: an HMAC-SHA256 of the raw JSON body, made with the
secret key of the endpoint.
Compute it over the raw body you received, not over JSON that you serialized again.
When a delivery fails, Repejo tries again and waits longer each time. It makes up to 20 attempts over about twelve days.
Your endpoint must respond within 30 seconds. Repejo counts a slower response as a failed delivery and tries again later. Accept the event, for example by putting it in a queue, respond, and process it after that.
If the last attempt also fails, Repejo stops and marks the delivery as abandoned.
Use this flow when Repejo collects and signs the Autogiro mandate, but your own CRM or backend talks to Bankgirocentralen and charges the donor. For example, Repejo runs the face-to-face or website checkout, and your system sets up the Autogiro payments.
Create an HTTPS endpoint in your system that accepts POST requests with a raw JSON
body. Verify the
Repejo-Signature
header against the raw body before you parse the payload.
Answer 2xx when you have stored the event. Repejo tries again after any other answer.
Set up an endpoint in Repejo, as described at the top of this page, and select
both subscription.created
and subscription.updated.
You need both, because a subscription can change after Repejo creates it.
Your endpoint can also get subscriptions with other payment methods. Read
payload.data.payment_channel
and
payload.data.collected_by
and skip the events that you do not handle. An Autogiro
subscription on your own bankgiro has
payment_channel
"autogiro"
and collected_by
"organisation".
You can get the same subscription more than once: when it changes again, and when Repejo retries a failed delivery. Make your handler safe to run twice.
X-Repejo-Event-ID
to find a retry of an event that you already have.
X-Repejo-Event-ID
and gets a new X-Repejo-Delivery-ID.
The automatic retries of one delivery keep its delivery id (only
X-Repejo-Attempt
changes). If a resend must be ignored, find duplicates by event id. If it must be
processed again, use the delivery id.
data.id as the
id of the subscription in your CRM. It does not change.
subscription.updated
does not create a second mandate or payment instruction.
To link a subscription to a campaign or a source in your CRM, create a custom attribute in Repejo, for example a campaign id that points to a Salesforce campaign.
Repejo sends the value in
payload.data.metadata
of the subscription, so you can save it on the record in your CRM.
metadata
always contains every
defined custom attribute. An attribute without a value is an empty string "",
so every delivery has the same keys.
Campaign tags in the link to the checkout need no custom attribute. Every
utm_*
parameter and the ad click ids fbclid, gclid, gbraid,
wbraid
and msclkid
are in
payload.data.tracking
on payer, subscription and receivable events. Repejo keeps at most 10 tags, drops keys
longer than 50 characters and cuts values to 200 characters.
The test environment fakes every payment method, so you can test the whole flow there. Set up a payment method in the test environment, create a checkout that uses it, and give a recurring gift.
Make sure that your endpoint gets
subscription.created
and, when applicable,
subscription.updated
with the expected payment_channel,
collected_by
and metadata
values.