Skip to content

Onboard a cardholder ​

Create a customer organization, provision the cardholder's identity, collect agreement acceptance, and submit an application. Issue a card after the application is approved.

Provision a customer organization ​

The customer organization groups cardholders you manage. It is separate from your partner organization, which the API derives from your key.

graphql
mutation CreateCustomer($input: CreatePartnerCustomerOrganizationInput!) {
  createPartnerCustomerOrganization(input: $input) {
    success
    organizationId
    error
  }
}
json
{ "input": { "name": "Acme Corp" } }

Store organizationId after success: true. This operation has no idempotency key; do not repeat it blindly if the response is lost.

Provision a cardholder ​

Send the identity and contact details collected through your onboarding flow. addressCountryCode is an ISO 3166 alpha-2 code; phone fields contain digits and birthDate uses YYYY-MM-DD.

graphql
mutation CreateCardholder($input: CreatePartnerCardUserInput!) {
  createPartnerCardUser(input: $input) {
    success
    cardUserId
    reused
    errorCode
    errorMessage
  }
}
json
{
  "input": {
    "customerOrganizationId": "<organizationId>",
    "firstName": "Alice",
    "lastName": "Tester",
    "email": "[email protected]",
    "addressLine1": "1 Main St",
    "addressCity": "Brooklyn",
    "addressRegion": "NY",
    "addressPostalCode": "11201",
    "addressCountryCode": "US",
    "phoneCountryCode": "1",
    "phoneNumber": "5551234567",
    "birthDate": "1990-01-15",
    "nationalId": "<cardholder national identifier>"
  }
}

Store the returned cardUserId for the application. Provisioning is idempotent on (customerOrganizationId, email): the same pair returns the existing identifier with reused: true. Repeating the call does not update the stored profile.

All identity fields above are required. Blank fields, an invalid birth date, or an invalid country code return success: false with errorCode: "VALIDATION_ERROR" and details in errorMessage. Agio encrypts nationalId at rest and decrypts it when forwarding identity to the card processor.

Correct details before applying ​

patchPartnerCardUser changes supplied identity fields and keeps omitted fields. It accepts the provisioning fields except email and customerOrganizationId. The email is the provisioning idempotency key; the organization comes from the existing record.

graphql
mutation CorrectCardholder($input: PatchPartnerCardUserInput!) {
  patchPartnerCardUser(input: $input) {
    success
    errorCode
    errorMessage
  }
}
json
{
  "input": {
    "cardUserId": "<provisioned cardUserId>",
    "addressLine1": "2 Main St",
    "addressCity": "Brooklyn",
    "addressRegion": "NY",
    "addressPostalCode": "11201",
    "addressCountryCode": "US"
  }
}

The merged address must remain complete. After an application exists for this cardholder, the mutation returns APPLICATION_ALREADY_SUBMITTED. Use this correction step before submitting, including when an application attempt returns INCOMPLETE_ADDRESS.

Collect agreement acceptance ​

Present the applicable card agreements to the cardholder before submitting their application. Record what they accepted, its version and hashes, and when they accepted it. agreementsAcceptance is required; isTermsOfServiceAccepted: true is also required and does not replace the evidence.

Use the agreement set Agio supplies for your configured card program and jurisdiction. Confirm the applicable document keys during integration setup; the customer organization used to scope API records does not determine the legal agreement set.

Document keyRaw agreement
card-terms-usUS card terms
account-opening-privacy-usUS account-opening privacy notice
card-terms-internationalInternational card terms
card-terms-us-b2bUS business card terms
card-terms-international-b2bInternational business card terms
authorized-user-agreementAuthorized user agreement

Fetch each applicable raw Markdown document and hash the response bytes with SHA-256. Do not normalize whitespace or re-encode the text before hashing. Present that same text to the cardholder and send its lowercase hex hash. Confirm the current agreement-set version with Agio when configuring your integration.

bash
curl -fsS https://www.agiodigital.com/agio-digital-ltd-card-terms-us-program.md -o card-terms-us.md
shasum -a 256 card-terms-us.md
Acceptance fieldValue
agreementVersionPublished agreement-set version
documentHashesOne { key, sha256 } for each applicable document
acceptedAtISO 8601 timestamp from your acceptance record
endUserIpAddressOptional cardholder IP when accepting
endUserAgentOptional cardholder browser or application user agent when accepting

The end-user IP is the cardholder's address at acceptance, not your API server's IP. acceptedAt is your attestation; Agio records a separate server receipt time and retains consent evidence for five years. Missing evidence returns ATTESTATION_REQUIRED; invalid evidence returns a validation error. Correct the evidence before retrying.

Submit an application ​

Choose exactly one collateral-admin option:

  • createWallet: true uses your configured partner treasury as the collateral admin for this cardholder. It does not create a separate personal wallet. Your treasury must be configured by Agio first.
  • walletAddress supplies an EVM address whose owner will administer the collateral. Partner treasury withdrawal cannot sign for a customer's self-custody address.
graphql
mutation Apply($input: CreatePartnerCardApplicationInput!) {
  createCardApplicationForPartnerUser(input: $input) {
    success
    applicantId
    cardApplicationId
    cardApplicationExternalId
    applicationStatus
    applicationCompletionUrl
    walletAddress
    errorCode
    error
  }
}
json
{
  "input": {
    "cardUserId": "<provisioned cardUserId>",
    "createWallet": true,
    "occupation": "Software Developers",
    "annualSalary": "100000",
    "accountPurpose": "Business expenses",
    "expectedMonthlyVolume": "5000",
    "isTermsOfServiceAccepted": true,
    "agreementsAcceptance": {
      "agreementVersion": "<published agreement-set version>",
      "documentHashes": [
        { "key": "card-terms-us", "sha256": "<SHA-256 of presented US terms>" },
        { "key": "account-opening-privacy-us", "sha256": "<SHA-256 of presented privacy notice>" }
      ],
      "acceptedAt": "2026-10-04T06:00:00.000Z",
      "endUserIpAddress": "203.0.113.7",
      "endUserAgent": "PartnerApp/2.4"
    }
  }
}

The agreement keys above illustrate a US document set; replace them with the keys and hashes Agio supplies for your program. occupation accepts a supported SOC code or description; see occupation codes.

If your Sumsub workspace is registered with Agio, you can also supply a fresh partnerKycShareToken. Arrange that integration with Agio before using it. Agio uses the token in the KYC handover and still forwards the provisioned identity. An expired, used, invalid or ineligible token returns KYC_TOKEN_EXPIRED ("KYC share-token has expired. Please retry with a fresh token."); mint a fresh token for Agio as the recipient. Only Sumsub tokens are supported; other providers return KYC_PROVIDER_NOT_SUPPORTED. Workspace problems return KYC_WORKSPACE_NOT_AUTHORIZED. Without a share token, the stored identity is used directly.

Identifiers ​

IdentifierUse
Provisioned cardUserId / response applicantIdSubmit the application and identify your provisioned applicant
cardApplicationIdNumeric AgioCard_card_application.id; query the application and pass it to createCard
cardApplicationExternalIdThe same UUID as your provisioned cardUserId, stored as card_application_external_id
Application, card and transaction card_user_idThe same UUID as your provisioned cardUserId; use it for balance, funding and withdrawal calls

Your provisioned cardUserId identifies the cardholder on the application, its cards and its transactions, and it does not change. Partner-issued cards have no Agio user_id; their customer organization and card company provide the ownership scope.

Check application status ​

Query by the numeric ID returned on application creation. Its card_user_id is your provisioned cardUserId.

graphql
query ApplicationStatus($applicationId: Int!) {
  AgioCard_card_application(where: { id: { _eq: $applicationId } }, limit: 1) {
    id
    applicant_id
    card_application_external_id
    card_user_id
    application_status
    application_completion_link
    deposit_address
    deposit_chain_id
    updated_at
  }
}
json
{ "applicationId": 42 }

Poll at a modest interval, such as once a minute, or use application webhooks. This is client guidance, not a guaranteed per-application polling limit.

StatusAction
PENDINGWait for verification and provisioning
NEEDSINFORMATION, NEEDSVERIFICATIONGive the signed completion link to the cardholder
MANUALREVIEWWait for compliance review
APPROVED, ACTIVECreate a card when issuer cardholder data is ready
DENIED, CANCELEDStop issuance; contact Agio if the decision needs review

For NEEDSINFORMATION, completionUrl is the link to give the cardholder and nextStep is AWAIT_REVIEW. After the cardholder finishes, watch for card_application.status_changed. No resubmit call is needed. For declines, treat nextStep: TERMINAL as final and RESUBMIT as retryable.

applicationCompletionUrl from the mutation and webhook completionUrl are composed signed links: open them as returned. In application_completion_link, prefer completionUrl when present. Older records may contain url with a params object; append every parameter to the URL. If there are no parameters, use url. Stripping the signature or rebuilding a link from a cardholder ID prevents verification.

If the application request times out before you receive an ID, query AgioCard_card_application with where: { applicant_id: { _eq: "<provisioned cardUserId>" } } before retrying. Application creation does not supply an idempotency key.

Next: Create and manage cards.