Skip to content

Creating & Managing Cards ​

Once a card application is approved, you can create virtual and physical cards using the createCard mutation. This guide covers card creation, lifecycle management, and all available card operations.

Creating Cards ​

Creation Flow ​

The createCard Mutation ​

graphql
mutation CreateCard($input: CreateCardInput!) {
  createCard(input: $input) {
    success
    id
    cardId
    cardType
    status
    last4
    expirationMonth
    expirationYear
    error
  }
}

Creating a Virtual Card ​

Virtual cards are available as soon as they are created.

graphql
# Variables
{
  "input": {
    "cardApplicationId": 42,
    "cardType": "virtual",
    "limit": {
      "amount": 50000,
      "frequency": "per30DayPeriod"
    },
    "displayName": "JANE SMITH"
  }
}

Creating a Physical Card ​

Physical cards require a shipping address and phone number. The card ships to the provided address and must be activated upon receipt.

graphql
# Variables
{
  "input": {
    "cardApplicationId": 42,
    "cardType": "physical",
    "limit": {
      "amount": 100000,
      "frequency": "per30DayPeriod"
    },
    "displayName": "JANE SMITH",
    "shipping": {
      "firstName": "Jane",
      "lastName": "Smith",
      "phoneNumber": "+15555551234",
      "line1": "123 Main St",
      "city": "Miami",
      "region": "FL",
      "postalCode": "33101",
      "countryCode": "US",
      "method": "standard"
    }
  }
}

Input Fields ​

FieldTypeDescription
cardApplicationIdInt!ID of the approved card application
cardTypeCardType!"virtual" or "physical"
limitCardLimitInputSpending limit: { amount, frequency }
displayNameStringName printed on card (max 26 chars, alphanumeric + spaces + periods + hyphens). Immutable after creation.
shippingCardShippingInputRequired for physical cards. Ignored for virtual.
billingCardAddressInputBilling address. Defaults to shipping address if omitted.
sessionIdStringFrom generateEncryptionKeys (required if setting PIN)
encryptedPinStringPre-encrypted PIN (4-12 digits)

Spending Limit Frequencies ​

FrequencyDescription
per24HourPeriodRolling 24-hour window
per7DayPeriodRolling 7-day window
per30DayPeriodRolling 30-day window
perYearPeriodRolling 365-day window
perAuthorizationPer-transaction cap
allTimeLifetime total limit

Amounts in Cents

All limit amounts are in cents. 50000 = $500.00, 100000 = $1,000.00.

Card Status Lifecycle ​

StatusDescriptionReversible
notActivatedIssued but requires activation (physical cards)Yes
activeFully functional--
lockedTemporarily frozenYes (unfreeze)
canceledPermanently disabledNo

Card Management ​

Freeze a Card ​

Temporarily disable a card. The card cannot be used for transactions while frozen.

graphql
mutation FreezeCard($cardId: Int!) {
  freezeCard(cardId: $cardId) {
    success
    id
    cardId
    status
    error
  }
}

Unfreeze a Card ​

Re-enable a frozen card.

graphql
mutation UnfreezeCard($cardId: Int!) {
  unfreezeCard(cardId: $cardId) {
    success
    id
    cardId
    status
    error
  }
}

Cancel a Card ​

Permanently cancel a card. This action is irreversible.

graphql
mutation CancelCard($input: CancelCardInput!) {
  cancelCard(input: $input) {
    success
    id
    cardId
    status
    error
  }
}
graphql
# Variables
{
  "input": {
    "cardId": 42,
    "reason": "No longer needed"
  }
}

Replace a Virtual Card ​

Generate a new card number for an existing virtual card. The old card is canceled automatically and the new card inherits spending limits.

graphql
mutation ReplaceVirtualCard($cardId: Int!) {
  replaceVirtualCard(cardId: $cardId) {
    success
    id
    oldCardId
    newCard {
      id
      type
      status
      last4
      expirationMonth
      expirationYear
    }
    error
  }
}

Replace Any Card (Lost/Stolen/Damaged) ​

Replace a virtual or physical card with a reason code. Physical card replacements require a shipping address.

graphql
mutation ReplaceCard($input: ReplaceCardInput!) {
  replaceCard(input: $input) {
    success
    id
    oldCardId
    newCard {
      id
      type
      status
      last4
      expirationMonth
      expirationYear
    }
    error
  }
}
graphql
# Variables (physical card replacement)
{
  "input": {
    "cardId": 42,
    "reason": "lost",
    "shippingAddress": {
      "firstName": "Jane",
      "lastName": "Smith",
      "phoneNumber": "+15555551234",
      "line1": "123 Main St",
      "city": "Miami",
      "region": "FL",
      "postalCode": "33101",
      "countryCode": "US"
    }
  }
}

Replacement reasons: lost, stolen, damaged.

Update Spending Limit ​

graphql
mutation UpdateCardLimit($input: UpdateCardLimitInput!) {
  updateCardLimit(input: $input) {
    success
    id
    cardId
    limitAmount
    limitFrequency
    error
  }
}
graphql
# Variables
{
  "input": {
    "cardId": 42,
    "limitAmount": 200000,
    "limitFrequency": "per30DayPeriod"
  }
}

Rename a Card ​

Update the card's display nickname (max 26 characters).

graphql
mutation UpdateCardNickname($cardId: Int!, $nickname: String!) {
  updateCardNickname(cardId: $cardId, nickname: $nickname) {
    success
    id
    error
  }
}

Star/Unstar a Card ​

Mark a card as a favorite.

graphql
mutation UpdateCardStarred($cardId: Int!, $isStarred: Boolean!) {
  update_AgioCard_card_by_pk(pk_columns: { id: $cardId }, _set: { is_starred: $isStarred }) {
    id
    is_starred
  }
}

PIN Management ​

PINs are encrypted using RSA + AES-128-GCM. You must first generate an encryption session, then pass the sessionId with PIN operations.

Set or Update PIN ​

graphql
mutation SetCardPin($input: SetCardPinInput!) {
  setCardPin(input: $input) {
    success
    id
    error
  }
}
graphql
# Variables
{
  "input": {
    "cardId": 42,
    "sessionId": "session-id-from-generateEncryptionKeys",
    "encryptedPin": "encrypted-pin-value"
  }
}

PIN requirements: 4-12 digits, no repeated digits (e.g., 1111), no sequential patterns (e.g., 1234, 4321).

Reveal PIN ​

graphql
mutation GetCardPin($cardId: Int!, $sessionId: String!) {
  getCardPin(cardId: $cardId, sessionId: $sessionId) {
    success
    id
    encryptedPin
    error
  }
}

The response encryptedPin must be decrypted client-side using the session key.

Card Secrets ​

Reveal the full card number (PAN), CVC, and expiry. Secrets are returned encrypted and must be decrypted client-side.

graphql
mutation RevealCardSecrets($cardId: Int!, $sessionId: String!) {
  revealCardSecrets(cardId: $cardId, sessionId: $sessionId) {
    success
    id
    encryptedSecrets
    error
  }
}

The encryptedSecrets field contains a JSON string that, when decrypted, yields:

json
{
  "pan": "4111111111111234",
  "cvc": "123",
  "expiry": { "month": "12", "year": "2028" }
}

Security

Never store decrypted card secrets. Only request them when absolutely necessary for display to the cardholder.

Listing Cards ​

Query all cards for the authenticated user. type is the machine value (virtual / physical) to branch logic on; card_type_display is the human-readable label to render:

graphql
query vwCards($where: AgioCard_vw_card_bool_exp, $limit: Int = 100, $offset: Int, $includeBalance: Boolean! = false) {
  cards: AgioCard_vw_card(where: $where, order_by: [{ last_used_at: desc_nulls_last }, { status: asc }, { created_at: desc }], limit: $limit, offset: $offset) {
    id
    type
    card_type_display
    status
    last4
    nickname
    is_starred
    created_at
    last_used_at
    balance @include(if: $includeBalance) {
      credit_limit
      spending_power
      pending_charges
      posted_charges
      balance_due
    }
  }
}

Next Steps ​

Creating & Managing Cards has loaded