Skip to main content
PDPC = Personal Data Processing Consent. While TPA is signed by the company’s authorized reps, PDPC is signed by the individual cardholder (employee). It’s the GDPR “yes, you can process my transaction data” consent.
This page covers Path A (email + eID). If the person already exists in OpenCard, use Path B (identity_id) instead — no email, no waiting. → Card Holder Onboarding

Prerequisites

Before creating card holders:
  1. ✅ TPA created (ideally fully signed)
  2. ✅ Organization created and linked to that TPA

Create card holder (Path A — with email)

POST /accounts/{accountId}/organizations/{organizationId}/cardholders
Scope: card-holders-write
{
  "reference_id": "employee_john_42",
  "email": "john@acme.se",
  "language": "sv",
  "skip_pdpc_email": false
}
FieldRequiredNotes
reference_idYour internal user ID — comes back in every webhook
email✅*Required unless identity_id provided
identity_id✅*Link existing identity from TPA (skips email if already identified)
skip_pdpc_emailtrue = no email sent (you handle consent yourself)
languagePDPC legal text language. Defaults from TPA country

What happens server-side

  1. Card holder record created
  2. PDPC record created, linked to TPA + card holder
  3. PDPC token = random 40-char string
  4. Legal text snapshot from latest type=pdpc template
  5. If identity_id provided → immediately identify() + card_holder.identified webhook
  6. Unless skip_pdpc_emailemail queued 📧

The email

SubjectWe need your approval.
Linkhttps://{env}/accounts/{accountId}/pdpcs/{pdpcId}/sign/{token}
LanguageCard holder’s language setting

Cardholder signs PDPC

Web flow (not API). Cardholder:
  1. Clicks email link
  2. Reads PDPC legal text
  3. Checks ✅ “I have read the text above”
  4. Clicks “Approve & Identify”
  5. Assently CoreID loads in auth mode (identity verification, not document signing)
  6. Signs with eID — see eID Signing
  7. Completes eID
Server-side:
  1. Validates identityToken from Assently
  2. IdentityService::findOrCreateBySsn(ssn, country, client)
  3. Links identity to card holder → card_holder.identified webhook (if not already)
  4. CardHolderService::signPdpc():
    • signed=true, signed_at=now()
    • Encrypted ssn + name stored on PDPC
    • Signed PDF generated → cloud storage
    • 📧 Email with signed PDF to cardholder
    • 🔔 card_holder.signed.pdpc webhook

Card holder lifecycle webhooks

EventWhenYour action
card_holder.createdAPI createStore mapping reference_id → OpenCard
card_holder.identifiedeID identity linkedUser verified
card_holder.signed.pdpcPDPC signedStart expecting transactions for this user
card_holder.deletedAPI deleteRemove from your system
card_holder.email.deliveredPDPC email deliveredTrack delivery
card_holder.email.failedEmail bouncedResend or fix email

Update / resend

PUT .../cardholders/{cardHolderId}
If PDPC is unsigned or no identity linked, and skip_pdpc_email=false → resends PDPC email.
{
  "reference_id": "employee_john_42",
  "email": "john.newemail@acme.se",
  "skip_pdpc_email": false,
  "language": "sv"
}

Delete card holder

DELETE .../cardholders/{cardHolderId}
Fires card_holder.deleted webhook. Transactions already delivered stay in your system — you handle cleanup.
Templates use placeholders replaced at creation time:
PlaceholderReplaced with
{{issuer.name_display}}Card issuer display name
{{organization.name}}{tpa.name} ({tpa.organization_number})
{{issuer.name_legal}}Card issuer legal name
{{system.name}}{account.name_system}, {account.name_legal} ({account.organization_number})