Open dashboardwalletap.com

Access badges over the API

Invite people to an access badge, read who holds one and the card numbers your doors need, and suspend or revoke badges from your own systems.

Web

An access badge is a door credential in Apple Wallet or Google Wallet. You design and publish a badge in the dashboard; the API then hands it out and takes it away, so an HR system can give staff their badge on their first day and remove it on their last.

Enterprise

Before you start#

  • Your account has access badges, and at least one badge is published. See Access badges to design and publish one.
  • An API key with badge scopes. The Issue access badges preset can invite people and read holders. Suspending, revoking and removing need badges.revoke, which only Full access or a custom key holds — deliberately, since handing out a door credential and taking one away are different powers.

The words#

  • A badge (in the API, a badge template) is the design and configuration, such as Harbour HQ Staff.
  • A holder is a person who has been given one.
  • A badge pass is the credential itself, on one holder's phone. A holder who changes phones ends up with a new badge pass.
  • An invite is a single-use link that gives one person one badge pass.

Scopes#

ScopeAllows
badges.viewAllListing badges, invites, holders and their badge passes
badges.issueInviting someone, and cancelling an invite
badges.editRenaming a holder, refreshing a badge pass's design
badges.revokeSuspending, resuming and revoking badge passes, removing a holder

Find the badge's ID#

GEThttps://api.walletap.com/badge/templates
Shell
curl https://api.walletap.com/badge/templates \
  -H "X-API-Key: $WALLETAP_API_KEY"
JSON
{
  "templates": [
    { "id": "badge_123", "name": "Harbour HQ Staff", "active": true, "appleStatus": "active", … }
  ]
}

active is true for a published badge. GET /badge/templates/{id} returns one badge.

Invite someone#

POSThttps://api.walletap.com/badge/templates/{id}/invites
Shell
curl -X POST https://api.walletap.com/badge/templates/badge_123/invites \
  -H "X-API-Key: $WALLETAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sofia.rossi@example.com",
    "firstName": "Sofia",
    "lastName": "Rossi",
    "days": 14,
    "sendToEmail": true
  }'
FieldNotes
email, phoneWho the badge is for. At least one is required. A phone number alone identifies the person too
firstName, lastNameThe name on the badge
daysHow long the link works, 1 to 365. 30 by default
sendToEmailtrue emails the link. Off unless you set it
sendToPhonetrue texts the link over the account's messaging channels. Off unless you set it
JSON
{
  "code": "Kq3xR2a",
  "url": "https://walletap.com/b/Kq3xR2a",
  "email": "sofia.rossi@example.com",
  "phone": null,
  "expiresAt": 1761390000000,
  "days": 14,
  "reused": false,
  "delivery": { … }
}

Unlike issuing a pass, inviting someone sends nothing unless you ask: with both flags left out, you get the link back and deliver it yourself. delivery reports how each requested channel went; a failed send still leaves a working link.

Warning

The url is the credential. Whoever opens it first can add the badge to their phone, so send it only to the person it names.

How invites behave:

  • One live link per person. Inviting the same person again, before they have added the badge, returns the same link — and sends it again if you ask — with reused set to true. To replace a link instead, cancel the invite first.
  • One badge per link. Once the badge is in a wallet, the link cannot add another. Inviting the person again then makes a new link, for a new phone or after they removed the badge.
  • Nothing is used up until the link is opened. An invite nobody opens costs nothing.
List and cancel invites#

GET /badge/templates/{id}/invites lists the badge's invite links, newest first, each with its url, email and expiresAt. A link stays in the list after it is used or expires; cancelling the invite removes it.

To withdraw one, send the person's email:

Shell
curl -X DELETE https://api.walletap.com/badge/templates/badge_123/invites \
  -H "X-API-Key: $WALLETAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "sofia.rossi@example.com" }'

The link stops working. A badge already in someone's wallet is left alone — remove the holder to take that away.

List holders#

GEThttps://api.walletap.com/badge/users
Shell
curl "https://api.walletap.com/badge/users?badgeTemplateId=badge_123" \
  -H "X-API-Key: $WALLETAP_API_KEY"
JSON
{
  "users": [
    {
      "id": "holder_123",
      "email": "daniel.okafor@example.com",
      "phone": null,
      "name": "Daniel Okafor",
      "active": true,
      "credentials": [
        {
          "passId": "bp_123",
          "platform": "APPLE_WALLET",
          "format": "H10301",
          "bits": 26,
          "facilityCode": 1,
          "cardNumber": 38,
          "wiegandHex": "202004C",
          "keyset": "…"
        }
      ]
    },
    {
      "id": null,
      "email": "sofia.rossi@example.com",
      "name": "",
      "invited": true,
      "invitedAt": 1760180400000,
      "expiresAt": 1761390000000,
      "inviteUrl": "https://walletap.com/b/Kq3xR2a",
      "credentials": []
    }
  ]
}

Leave out badgeTemplateId to list holders of every badge on the account.

  • Someone invited who has not added their badge yet has id: null and invited: true.
  • credentials lists the badges in the holder's wallet right now, as a door controller or locker enrols them: format, facilityCode and cardNumber, or the whole frame in wiegandHex. One call gives you every number your access system needs.
  • credentials: null means the numbers could not be read just now. Retry; never treat it as "no badges", or you would lock someone out.

A card number belongs to one badge pass. When someone gets a new badge, they get a new number, so update anything you configured with the old one.

One holder's badge passes#
GEThttps://api.walletap.com/badge/users/{userId}/passes

Returns each badge pass the holder has from your badges, with its id, status, platform, issuedAt and credentials. badgeTemplateId narrows it to one badge.

statusMeans
ACTIVEIn the wallet and opening doors
CREATED, ISSUE_INITIATED, ISSUINGBeing added to the wallet
SUSPENDED, USER_SUSPENDEDPaused, not opening doors
REVOKED, USER_REVOKED, CANCELLED, EXPIREDEnded for good
ISSUE_FAILEDAdding it to the wallet failed, so it never opened a door

Suspend, resume or revoke a badge pass#

POSThttps://api.walletap.com/badge/passes/{passId}/lifecycle
Shell
curl -X POST https://api.walletap.com/badge/passes/bp_123/lifecycle \
  -H "X-API-Key: $WALLETAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "suspend", "reason": "Lost phone reported" }'
actionDoesScope
suspendStops the badge opening doors, until it is resumedbadges.revoke
resumeTurns a suspended badge back onbadges.revoke
revokeEnds the badge for good and removes it from the wallet. The person needs a new invite to get anotherbadges.revoke
refreshPushes the badge's current design into the wallet. Access is unchangedbadges.edit

For suspend and resume, reason (up to 200 characters) is kept in the credential's audit trail. The call answers 202: the change takes effect a moment later, so read the badge pass back with GET /badge/users/{userId}/passes to see its new status.

Rename a holder#

PATCHhttps://api.walletap.com/badge/users/{userId}
Shell
curl -X PATCH https://api.walletap.com/badge/users/holder_123 \
  -H "X-API-Key: $WALLETAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lastName": "Okafor-Novak" }'

Send firstName, lastName or both. The new name is pushed to every badge the person holds from you; refreshed in the response lists them.

Remove a holder#

DELETEhttps://api.walletap.com/badge/users/{userId}
Shell
curl -X DELETE https://api.walletap.com/badge/users/holder_123 \
  -H "X-API-Key: $WALLETAP_API_KEY"

This is offboarding in one call: every badge you issued the person is revoked, and every invite you sent them is withdrawn. If they also hold a badge from another Walletap account, that badge is untouched and holderKept is true.

If something goes wrong#

403 "access badges not enabled" — the account does not have access badges. See plans.

403 "Missing permission: badges.issue" (or another badge scope) — the key lacks that scope. See Scopes.

409 "not_published" — the badge has not been published yet. Publish it in the dashboard; invites work from then on.

400 "a valid email address or phone number is required" — send a well-formed email or phone.

404 "not found" — the badge, holder or badge pass does not exist or is not one of yours. A badge ID from another account can also answer 403 "forbidden".

502 with an error such as users_list_failed or lifecycle_failed — the wallet credential service did not answer. Retry after a short wait.

Next steps#