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.
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_idandbilling_idset -
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
