Skip to main content

Cards

Use the cards and card queries and the card lifecycle mutations to manage your members' First Dollar cards. You can list and inspect cards, issue new ones, activate them, lock and unlock them, and cancel them — with an optional replacement issued in the same call.

Cards are issued to an Individual for a Benefits Program. A card spends from the Benefits in its Benefits Program; it is not tied to a single Benefit.

The card lifecycle

A card moves through four statuses:

StatusMeaning
UNACTIVATEDIssued but not yet activated by the cardholder.
ACTIVEActive and usable.
SUSPENDEDTemporarily locked; transactions are blocked until unlocked.
TERMINATEDPermanently cancelled; cannot be reactivated.

The mutations drive the transitions: issueCard creates an UNACTIVATED card, activateCard makes it ACTIVE, lockCard and unlockCard move it between ACTIVE and SUSPENDED, and cancelCard ends it at TERMINATED.

Physical card delivery is asynchronous. Track it with the card's fulfillmentStatus, which progresses from ISSUED through ORDERED to SHIPPED. If fulfillment fails, the status is REJECTED; a re-ordered card reports REORDERED. Virtual cards report DIGITAL. See CardFulfillmentStatus for the full set.

What you need

List cards

The cards query returns cards across all of your members by default, newest first. Narrow it to specific members with where.individualIds.

query Cards($where: CardsFilterInput, $after: String, $first: Int) {
cards(where: $where, after: $after, first: $first) {
... on CardsResults {
nodes {
id
individualId
benefitsProgramId
userType
cardholderName {
firstName
lastName
}
last4
expiration
type
status
fulfillmentStatus
}
pageInfo {
hasNextPage
endCursor
}
}
... on BadRequestError {
message
code
retryable
}
... on InternalServerError {
message
code
retryable
}
}
}

Variables:

{
"where": { "individualIds": ["<individualId>"] },
"first": 25
}

A successful response:

{
"data": {
"cards": {
"nodes": [
{
"id": "<cardId>",
"individualId": "<individualId>",
"benefitsProgramId": "<benefitsProgramId>",
"userType": "ACCOUNT_HOLDER",
"cardholderName": {
"firstName": "Jordan",
"lastName": "Smith"
},
"last4": "1234",
"expiration": "0530",
"type": "PHYSICAL",
"status": "ACTIVE",
"fulfillmentStatus": "SHIPPED"
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "<cursor>"
}
}
}
}

A member with no cards yet returns an empty nodes list. expiration is in MMYY format: "0530" is May 2030.

Pagination

The list is cursor-paginated. To fetch the next page, pass the previous page's endCursor as after and check hasNextPage. Cursors are opaque; do not parse or construct them.

Filters

All filters are optional. Omitted filters do not restrict the result. Filters that match nothing return an empty list, not an error.

FilterBehavior
individualIdsOnly cards belonging to these Individuals.
benefitsProgramIdsOnly cards associated with these Benefits Programs.
benefitIdsOnly cards whose Benefits Program contains one of these Benefits.
benefitTypesOnly cards whose Benefits Program contains a Benefit of one of these types (for example, HSA).
cardIdsOnly these specific cards.
typesOnly cards of these types: PHYSICAL, VIRTUAL.
statusesOnly cards in these statuses: UNACTIVATED, ACTIVE, SUSPENDED, TERMINATED.
last4Only cards whose last four digits match one of these values. Useful for support lookups.
userTypeWhich cardholders' cards to return. Defaults to ACCOUNT_HOLDER_ONLY; see Dependent cards.

For example, to list a member's active physical HSA cards:

{
"where": {
"individualIds": ["<individualId>"],
"benefitTypes": ["HSA"],
"types": ["PHYSICAL"],
"statuses": ["ACTIVE"]
}
}

Get a card

Fetch a single card with the card query, using an id returned by the list query or a mutation.

query Card($where: CardFilterInput!) {
card(where: $where) {
... on Card {
id
individualId
benefitsProgramId
userType
cardholderName {
firstName
middleName
lastName
}
last4
expiration
type
status
fulfillmentStatus
}
... on BadRequestError {
message
code
retryable
}
... on InternalServerError {
message
code
retryable
}
}
}

Variables:

{
"where": {
"id": "<cardId>",
"individualId": "<individualId>"
}
}

individualId is optional. When you pass it, the card is only returned if it belongs to that Individual; a card that belongs to someone else returns the same not-found response as a card that does not exist.

Issue a card

Issue a new card with issueCard. The mutation is idempotent: use a new idempotencyKey for each new card, and reuse the same key when retrying a failed request.

mutation IssueCard($input: IssueCardInput!) {
issueCard(input: $input) {
... on IssueCardResult {
card {
id
status
type
fulfillmentStatus
}
}
... on BadRequestError {
message
code
retryable
}
... on InternalServerError {
message
code
retryable
}
}
}

Variables:

{
"input": {
"individualId": "<individualId>",
"benefitsProgramId": "<benefitsProgramId>",
"type": "PHYSICAL",
"idempotencyKey": "<your unique key>"
}
}

The new card starts UNACTIVATED. For physical cards, poll fulfillmentStatus to follow the card's journey to the cardholder. Activate the card once it is received.

Card availability depends on the Benefits Program's configuration and the member's enrollment and account state. A request the program cannot fulfill returns a BadRequestError.

Activate a card

Activate a received card with activateCard.

mutation ActivateCard($input: ActivateCardInput!) {
activateCard(input: $input) {
... on ActivateCardResult {
card {
id
status
}
}
... on BadRequestError {
message
code
retryable
}
... on InternalServerError {
message
code
retryable
}
}
}

Variables:

{
"input": {
"cardId": "<cardId>",
"individualId": "<individualId>"
}
}

On success, the returned card's status is ACTIVE.

As on the detail query, individualId is optional on every lifecycle mutation: when you pass it, the operation only proceeds if the card belongs to that Individual.

Lock and unlock a card

Lock a card with lockCard to block new transactions — for example, while a member looks for a misplaced card. Locks with reason LOST, STOLEN, or OTHER are temporary and reversible with unlockCard. If the card is gone for good, cancel it instead.

Fraud locks are permanent

A lock with reason FRAUDULENT_CHARGES cannot be undone: unlockCard returns a BadRequestError explaining that the card stays locked. To restore card access for the member, cancel the card and issue a replacement with reissue: true.

mutation LockCard($input: LockCardInput!) {
lockCard(input: $input) {
... on LockCardResult {
card {
id
status
}
}
... on BadRequestError {
message
code
retryable
}
... on InternalServerError {
message
code
retryable
}
}
}

Variables:

{
"input": {
"cardId": "<cardId>",
"reason": "LOST"
}
}

reason is one of LOST, STOLEN, FRAUDULENT_CHARGES, or OTHER — see CardActionReason. A locked card reports status: SUSPENDED.

unlockCard takes only the cardId (and the optional individualId) and returns the card to ACTIVE. A card locked with reason FRAUDULENT_CHARGES cannot be unlocked — see the warning above.

Cancel or replace a card

Cancel a card permanently with cancelCard. To replace it — the common path for a lost or stolen card — set reissue: true and a new card is issued in the same operation. The mutation is idempotent; replaying the same idempotencyKey returns the same result without cancelling or issuing twice.

mutation CancelCard($input: CancelCardInput!) {
cancelCard(input: $input) {
... on CancelCardResult {
cancelledCardId
reissuedCardId
}
... on BadRequestError {
message
code
retryable
}
... on InternalServerError {
message
code
retryable
}
}
}

Variables:

{
"input": {
"cardId": "<cardId>",
"reason": "STOLEN",
"reissue": true,
"idempotencyKey": "<your unique key>"
}
}

A successful response:

{
"data": {
"cancelCard": {
"cancelledCardId": "<cardId>",
"reissuedCardId": "<new cardId>"
}
}
}

reissuedCardId is the replacement card's ID; fetch it with the card query to follow its fulfillment. When reissue is omitted or false, reissuedCardId is null. Cancellation is permanent — a TERMINATED card cannot be reactivated.

Dependent cards

Dependents get cards through the same issueCard mutation: pass the dependent's own Individual ID as individualId. The dependent must already exist — create one with createDependent. Dependent cards support physical cards only; requesting a virtual card for a dependent returns a BadRequestError.

Every lifecycle mutation works the same way on a dependent card.

By default, the list query returns account holders' cards only. Use the userType filter to include dependents:

{
"where": {
"individualIds": ["<individualId>"],
"userType": "INCLUDE_DEPENDENTS"
}
}

Each returned card's userType field reports ACCOUNT_HOLDER or DEPENDENT. A userType filter other than ACCOUNT_HOLDER_ONLY requires individualIds.

Errors

Returns BadRequestError when:

  • The card cannot be found. The cardId is unknown, does not belong to your organizations' members, or does not belong to the supplied individualId.
  • The card cannot be issued. The Benefits Program does not offer the requested card type, or the member's enrollment or account state does not allow a new card.
  • The operation does not apply to the card's status. For example, activating a cancelled card.

Unexpected failures return InternalServerError. For issueCard and cancelCard, retry safely with the same idempotencyKey.

See also

  • Statements — monthly account statements for an Individual.
  • Tax Forms — 1099-SA and 5498-SA data for an Individual, plus secure PDF downloads.
  • Debit Card Widget — an embeddable UI that lets members manage their own card, including secure card number display.