How to issue hotel key cards with an API
Issue a key card in 60 seconds
1# A two-night stay. In your PMS integration these come from the reservation.
2CHECK_IN=$(date -u +%Y-%m-%dT15:00:00Z)
3CHECK_OUT=$(date -u -v+2d +%Y-%m-%dT11:00:00Z 2>/dev/null || date -u -d '+2 days' +%Y-%m-%dT11:00:00Z)
4
5# 1. Grant card access to the room and the lobby for the stay.
6curl -X POST "https://connect.getseam.com/access_grants/create" \
7 -H "Authorization: Bearer $SEAM_API_KEY" \
8 -H "Content-Type: application/json" \
9 -d '{
10 "user_identity": {
11 "full_name": "Jane Doe",
12 "email_address": "jane@example.com"
13 },
14 "acs_entrance_ids": ["f74e4879-5991-4e2f-a368-888983dcfbfc"],
15 "requested_access_methods": [{ "mode": "card" }],
16 "starts_at": "'"$CHECK_IN"'",
17 "ends_at": "'"$CHECK_OUT"'"
18 }'
19
20# 2. Write the card access method onto a blank card with an encoder at the
21# property. Get the access_method_id from /access_methods/list first.
22curl -X POST "https://connect.getseam.com/access_methods/encode" \
23 -H "Authorization: Bearer $SEAM_API_KEY" \
24 -H "Content-Type: application/json" \
25 -d '{
26 "access_method_id": "27d8ad77-55c2-4e20-b5b3-43555926f0e8",
27 "acs_encoder_id": "d2f5cb38-3f42-4c92-8f6a-4b8de32b6b45"
28 }'Build it with your AI agent
- Reservation AutomationsPush reservations to Seam and let it manage codes across the booking lifecycle.
- Access GrantsPer-entrance, per-credential access: PIN codes, mobile keys and Instant Keys.
- Access CodesDirect, manual control of individual time-bound codes on a specific device.
1. Install the plugin
/plugin marketplace add seamapi/seam-plugin
/plugin install seam@seamapiThe Claude Code install also adds our documentation MCP server, so the agent can look up endpoints and device capabilities while it works. Cursor, Codex and other agents get the skills through npx skills add and can add the MCP server there separately.
2. Paste this prompt
Using the Seam Access Grants skill, build key card issuance for my hotel app. In a sandbox workspace: create a user identity for a guest, create an access grant on the room and lobby entrances with requested_access_methods [{ mode: "card" }] for a two-night stay, list the encoders on the access system with /acs/encoders/list, then encode the card access method with /access_methods/encode and confirm is_issued flips to true. Finish with the express-checkout story: what happens to the card at ends_at, and how I re-encode the same plastic for the next guest.What you can build
Front desk software that cuts keys
Check-in creates the grant and encodes the card in one flow, from your own UI. The desk agent taps one button; nobody re-types room numbers into the access system's console.
Kiosk and express check-in
A kiosk with an encoder issues keys with no staff at all: the reservation lookup creates the grant, the kiosk encodes, the guest walks to the elevator. Late arrivals stop depending on the night shift.
Cards that expire with the stay
The credential dies at ends_at whether or not the card comes back, and an extended stay is one update call, not a trip to the desk for new plastic. Lost cards are a deletion, not a re-key.
How it works
Create an Access Grant with a card access method
The guest, the entrances their stay covers, and the window. Seam creates the card credential on the access system, and the access method comes back with is_issued false: the credential exists but is not on plastic yet. That false is your cue to encode.
1const accessGrant = await seam.accessGrants.create({ 2 user_identity_id: guest.seamUserIdentityId, 3 acs_entrance_ids: [room.entranceId, lobby.entranceId], 4 requested_access_methods: [{ mode: "card" }], 5 starts_at: reservation.checkIn, 6 ends_at: reservation.checkOut, 7}) 8 9const [cardMethod] = await seam.accessMethods.list({ 10 access_grant_id: accessGrant.access_grant_id, 11}) 12 13// Not on plastic yet: is_issued stays false until a card is encoded. 14console.log(cardMethod.is_issued) // falseFind the encoder at the desk
Encoders are devices on the access system. List them with /acs/encoders/list and let the desk agent pick theirs; a kiosk pins its own encoder id in config. Systems that assign credentials to pre-registered cards skip the encoder entirely, and the grant is the whole flow.
1const encoders = await seam.acs.encoders.list({ 2 acs_system_id: reservation.acsSystemId, 3}) 4 5// A property usually has one per desk. Let the agent pick theirs. 6const frontDesk = encoders[0]Encode the card
Place a blank card on the encoder and call /access_methods/encode with the access method and the encoder. Confirm the result before handing plastic to a guest: is_issued flips to true once the write lands, and the common failures are a missing card on the encoder or a card type the system does not accept.
1await seam.accessMethods.encode({ 2 access_method_id: cardMethod.access_method_id, 3 acs_encoder_id: frontDesk.acs_encoder_id, 4}) 5 6// Confirm before handing the card over: poll the access method or listen 7// for its webhook. is_issued true means the credential is on the plastic.Let checkout happen on its own
No return step. The credential expires at ends_at, so an unreturned card is dead plastic, and revoking early is deleting the grant. Reuse returned cards by encoding a fresh access method onto them; one access method writes to one card, so each card gets its own.
1// Checkout needs no API call: the card stops opening doors at ends_at, 2// whether or not it comes back to the desk. 3 4// To reuse the plastic for the next guest, encode a new access method onto 5// it. An access method is written to one card only, so each card gets its 6// own, even for the same grant.
Frequently asked questions
Do I need a card encoder?
For systems that write credentials onto blank cards, yes: Seam drives the encoder hardware the property already has. Other systems assign the credential to a card that is already registered, and no encoder is involved. The capability page lists which systems work which way.
Can one card open several doors?
Yes. Put every entrance on the Access Grant, or pass a space that covers them, and the card is encoded for all of them at once. Adding a door later updates the credential.
What happens to a card at checkout?
The credential expires with the grant, so the card stops opening doors at ends_at whether or not it comes back to the desk. Revoking early is a deletion, not a trip to the lock.
Can I reuse cards between guests?
Yes. Re-encode the card with the next guest's access method. What you cannot do is write one access method onto two cards: a second card for the same guest gets its own access method on the same grant.
How do I check what is on a card?
Scan it on the encoder. The scan returns the card's encoded parameters, which is how a desk agent answers "why does this card not work" without guessing: wrong stay, expired credential and blank card all read differently.
Which access systems does this work with?
Card-based hotel and multifamily systems including ASSA ABLOY Visionline and Salto Space, with the full list on the plastic-card capability page. Support is per system, so check the system behind your doors, not the lock brand on them.
Related
- Smart locks that support plastic card encodingEvery model and system Seam encodes cards for, with live per-system status.
- Working with card encoders and scannersThe full encoding flow, scanning, and the common encoding errors.
- How to add mobile keys to your appThe phone-based sibling of this flow, for guests who skip the desk entirely.
- How to generate temporary access codes programmaticallyPIN codes on the same grants, for doors with keypads.