tpa.signed lands in your app.
What each object does
None of them is optional. See Common mistakes for what breaks without each.
1. Create the billing profile
Response → save
id as billing_id.
One billing profile can be shared by several organizations. Every organization with the same
billing_id becomes one line on your OpenCard invoice. Use that when a client has multiple legal entities or departments but one finance function. → Model: Billing2. Create the organization
Response → save
id as organization_id.
Card types
OpenCard sends transaction events only for the card types you turn on for this organization. A card of a type left atfalse produces no card.transaction.* events, and no enrichment on that transaction (receipts, true VAT, line items, Åland Index). Card holder events and tpa.signed are not filtered this way.
All three default to
false. Set all three to true when you create the organization so transactions start arriving. Turn a type off later with PUT if that client should not receive it.
Already created an organization without billing_id?
Fix it with a PUT — no need to delete and recreate:
One client, several organizations?
Usually one client = one organization. Split only when the client needs separate webhook endpoints, separate event subscriptions, or separate card-holder groups. All of them can point at the sametpa_id and billing_id.
API reference → Create organization · Model: Organization
3. Create the webhook
secret — save it, you need it to answer the challenge.
OpenCard immediately sends GET {url}?challenge=… with X-Event: challenge. Your endpoint answers with X-Verify-Token: HMAC-SHA256(challenge, secret). When that passes, the webhook is active and events start flowing.
Verify:
active: true. If it is false, the challenge failed — see Webhook setup for the handshake in detail, authentication headers and the test endpoint.
API reference → Create webhook · Full guide → Webhook setup · Event reference
Done — what you should have
And on the OpenCard side:
-
GET .../organizations/{organizationId}showstpa_id,billing_id, and the card types you want astrue -
GET .../organizations/{organizationId}/webhooksshows one webhook withactive: true
tpa.signed on the webhook you just created.
Common mistakes
Using the ocTPA plugin?
The plugin hands youbilling, tpa and tpa_signatories in onDataSend. It does not create the organization or the webhook. Do steps 2 and 3 in your onDataSend handler right after the billing and TPA calls. → Plugins
