Skip to main content
This is the full path: register → TPA signed → org live → cardholder consented → webhook firing. Grab coffee. ☕ Let’s go.

Step 0: Register (sandbox)

👉 sandbox-api.opencard.io/register You get an Account + admin user. Verify email. Log in. Your accountId is in the dashboard URL or API responses.

Step 1: Create an account_client

# Dashboard: Account → OAuth Clients → Create
# This is an account_client — machine-to-machine credential for your EMS integration.

# Then get a token:
curl -X POST https://sandbox-api.opencard.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=account-tpas-write account-tpas-read account-tpa-signatories-write organizations-write organizations-read card-holders-write webhooks-write webhooks-read public-records-read account-card-issuers-read account-card-issuers-write"
Save the access_token. All subsequent calls:
Authorization: Bearer {access_token}
Scopes are space-separated. Request only what you need. Full list in Authentication.

Step 2: Enable a card issuer

# List available issuers
curl https://sandbox-api.opencard.io/api/v1/application/cardissuers \
  -H "Authorization: Bearer $TOKEN"

# Attach one to your account (use the cardIssuerId from above)
curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/cardissuers/1 \
  -H "Authorization: Bearer $TOKEN"

Step 3: Lookup company (optional but smart)

Before creating a TPA, check the public registry for signing combinations:
curl "https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/publicrecords?country=SE&organization_number=5561234567" \
  -H "Authorization: Bearer $TOKEN"
Response includes signature_combinations — who can legally sign for this company. You’ll add those people as TPA signatories next.

Step 4: Create TPA

TPA = Transaction Processing Authorization. Legal permission for transaction data to flow.
curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/tpas \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "card_issuer_id": 1,
    "name": "Acme AB",
    "country": "SE",
    "organization_number": "5561234567",
    "language": "sv"
  }'
What happens server-side:
  1. Fetches company info from public registry (Roaring API in prod, test data in sandbox)
  2. Overrides name with registry name if found
  3. Creates TPA record with status pending-signatures
  4. Generates legal text snapshot from latest tpa template in that language
  5. Links card issuer + creates client record
Org number formats:
CountryFormatExample
🇸🇪 SE10 digits5561234567
🇳🇴 NO9 digits123456789
🇩🇰 DK8 digits12345678
🇫🇮 FIXXXXXXX-X1234567-8
Response → save tpaId.

Step 5: Add signatories → email goes out 📧

Each signatory = someone authorized to sign for the company.
curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/tpas/$TPA_ID/signatories \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ceo@acme.se",
    "name": "Anna Andersson"
  }'
What happens:
  1. Server generates a 40-char token for this signatory
  2. Email is queued immediately with subject: Authorise TPA for Acme AB
  3. Email contains signing link:
https://sandbox-api.opencard.io/accounts/{accountId}/tpas/{tpaId}/sign/{token}
  1. Signatory clicks link → sees TPA legal text → signs with eID
Repeat for each required signatory (check signature_combinations from step 3).
Signatories can only be updated/deleted while signed=false. Once signed, they’re locked.
Full signing details → TPA Flow and eID Signing.

Step 6: Wait for TPA to be signed ✍️

When all signatories have signed, OpenCard verifies signing rights and marks the TPA as signed. Then:
  • Signed PDF generated and stored
  • tpa.signed webhook fires (if subscribed)
  • TPA status → pending-activation (waiting for issuer to confirm cards)
  • Each signatory gets email with signed PDF attached

Step 7: Create organization

curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/organizations \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "client_acme_001",
    "tpa_id": '"$TPA_ID"',
    "name": "Acme AB"
  }'
reference_id = your internal client ID. It comes back in every webhook. Save organizationId.

Step 8: Configure webhook 🔔

curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/organizations/$ORG_ID/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/opencard",
    "enabled": true,
    "card_transaction_authorized": true,
    "card_transaction_cleared": true,
    "card_transaction_deleted": true,
    "card_holder_created": true,
    "card_holder_signed_pdpc": true,
    "receipt_fetched": true,
    "transaction_true_vat": true,
    "tpa_signed": true
  }'
Challenge happens immediately. OpenCard sends:
GET https://your-app.com/webhooks/opencard?challenge=RANDOM_STRING
Header: X-Event: challenge
Your endpoint must respond with header:
X-Verify-Token: HMAC-SHA256(challenge, webhook_secret)
Until challenge passes, active=false and no events are delivered. Full webhook setup → Webhook Setup

Step 9: Create card holders 👤

Two paths — pick one. Full guide → Card Holder Onboarding.

Path A: Email + eID (new employee)

curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/organizations/$ORG_ID/cardholders \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "employee_john_42",
    "email": "john@acme.se",
    "language": "sv"
  }'
John gets email → signs PDPC with eID → card_holder.identified → transactions flow.

Path B: identity_id (instant — person already in OpenCard)

# 1. List identities on the TPA (people who already have cards)
curl "https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/tpas/$TPA_ID/identities?is_card_holder=false" \
  -H "Authorization: Bearer $TOKEN"

# 2. Create card holder with identity_id — transactions start immediately
curl -X POST https://sandbox-api.opencard.io/api/v1/application/accounts/$ACCOUNT_ID/organizations/$ORG_ID/cardholders \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "employee_anna_07",
    "identity_id": 123,
    "skip_pdpc_email": true
  }'
card_holder.identified fires instantly + retroactive transaction webhooks replay. No waiting for user action. ⚡

Step 10: Receive your first transaction 🎉

Once the issuer delivers transaction states and the cardholder has signed PDPC:
// POST to your webhook URL
// Header: X-Event: card.transaction.authorized

{
  "id": "10",
  "state": "authorized",
  "receiptable": true,
  "card_issuer": "corporate-card",
  "original_amount": 109.38,
  "original_currency": "SEK",
  "purchase_merchant": "Espresso House",
  "purchase_date": "2026-06-08",
  "card_holder": { "reference_id": "employee_john_42" },
  "organization": { "reference_id": "client_acme_001" }
}
You did it. From zero to live transaction data.

What’s next?