Learn how to create a card access method and then encode this access method onto a plastic card.
Early Access Preview. The Access Grants and access methods APIs are currently in Alpha. We're actively developing them and seeking early feedback at [email protected]. Expect breaking changes as we refine the design.
Some access control systems require encoding a plastic card with the data necessary to enable access. This process involves creating an access grant that requests a card access method with the required access permissions and then using a card encoder to write the access method onto the card.
This process consists of the following basic steps:
Create an Access Grant that includes a request for a card access method and identify the ID of the resulting access method.
Use the /acs/encoders/list endpoint to retrieve a list of available encoders. Then, choose the encoder that you want to use to write the access method onto the card.
See Retrieve Encoders.
Use the /access_methods/encode endpoint to encode the access method onto the card, using the encoder that you have chosen.
See Encode the Card.
Once you have written an access method to a card, you cannot reuse the access method for another card. That is, you must create a separate access method for each card. However, you can reuse a card by re-encoding the card with a new access method.
1. Create an Access Grant
This example shows how to create a card access method as part of an Access Grant. Note that the is_encoding_required property of the resulting access method is true, which means that the access method must be encoded onto a card. Further, once you've encoded the access method on to the card, the is_issued property changes to true, and the issued_at property indicates the date and time at which the encoding occurred.
# Identify the IDs of the entrances to which
# you want to grant access.
entrances = seam.acs.entrances.list(
# Use the access system ID that you copied in the previous step.
acs_system_id=acs_system_id
)
# Create the Access Grant.
access_grant = seam.access_grants.create(
# Create a new user identity to represent your user.
user_identity={
"full_name": "Jane Doe",
"email_address": "[email protected]",
"phone_number": "+15555551000"
},
# Specify the IDs of the entrances to which you want to grant access.
acs_entrance_ids=[
entrances[0].acs_entrance_id,
entrances[1].acs_entrance_id
],
# Specify that you want to issue a card access method.
requested_access_methods=[
{"mode": "card"}
],
# Specify the access schedule.
starts_at="2025-08-01T15:00:00.000Z",
ends_at="2025-08-04T11:00:00.000Z"
)
Output
AccessGrant(
access_grant_id='6d74aefc-5712-4a8b-82c1-73a51ae60b87',
user_identity_id='8cc2633a-54ca-455a-8a2b-77e6a1fc4fee',
starts_at='2025-08-01T15:00:00.000Z',
ends_at='2025-08-04T11:00:00.000Z',
instant_key_url='https://ik.seam.co/ABCXYZ',
requested_access_methods=[
{
"display_name": "Plastic Card",
"mode": "card",
"created_at": "2025-06-16T16:54:19.946606Z",
# Note the access_method_id of the created card access method.
"created_access_method_ids": ["5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f"]
}
],
...
)
Code
# Identify the IDs of the entrances to which
# you want to grant access.
# Use the access system ID that you copied in the previous step.
mapfile -t entrances < <(
curl -s --request POST "https://connect.getseam.com/acs/entrances/list" \
--header "Authorization: Bearer $SEAM_API_KEY" \
--header "Content-Type: application/json" \
--data "{\"acs_system_id\": \"$acs_system_id\"}" |
jq -c '.acs_entrances[]'
)
# Create the Access Grant.
# In this command:
# - Create a new user identity to represent your user.
# - Specify the IDs of the entrances to which you want to grant access.
# - Specify that you want to issue a card access method.
# - Specify the access schedule.
access_grant=$(curl --include --request POST "https://connect.getseam.com/access_grants/create" \
-H "Authorization: Bearer $SEAM_API_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"user_identity\": {
\"full_name\": \"Jane Doe\",
\"email_address\": \"[email protected]\",
\"phone_number\": \"+15555551000\"
},
\"acs_entrance_ids\": [
\"$(echo ${entrances[0]} | jq -r '.acs_entrance_id')\",
\"$(echo ${entrances[1]} | jq -r '.acs_entrance_id')\"
],
\"requested_access_methods\": [
{
\"mode\": \"card\"
}
],
\"starts_at\": \"2025-08-01T15:00:00.000Z\",
\"ends_at\": \"2025-08-04T11:00:00.000Z\"
}")
Output
{
"access_grant":{
"access_grant_id":"6d74aefc-5712-4a8b-82c1-73a51ae60b87",
"user_identity_id":"8cc2633a-54ca-455a-8a2b-77e6a1fc4fee",
"starts_at":"2025-08-01T15:00:00.000Z",
"ends_at":"2025-08-04T11:00:00.000Z"
"instant_key_url":"https://ik.seam.co/ABCXYZ",
"requested_access_methods":[
{
"display_name":"Plastic Card",
"mode":"card",
# Note the access_method_id of the created card access method.
"created_access_method_ids":["5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f"],
"created_at":"2025-06-16T16:54:19.946606Z"
}
],
...
},
"ok":true
}
Code
// Identify the IDs of the entrances to which
// you want to grant access.
const entrances = await seam.acs.entrances.list({
// Use the access system ID that you copied in the previous step.
acs_system_id: acsSystemId
});
// Create the Access Grant.
const accessGrant = await seam.accessGrants.create({
// Create a new user identity to represent your user.
user_identity: {
full_name: "Jane Doe",
email_address: "[email protected]",
phone_number: "+15555551000"
},
// Specify the IDs of the entrances to which you want to grant access.
acs_entrance_ids: [
entrances[0].acs_entrance_id,
entrances[1].acs_entrance_id
],
// Specify that you want to issue a card access method.
requested_access_methods: [
{"mode": "card"}
],
// Specify the access schedule.
starts_at: "2025-08-01T15:00:00.000Z",
ends_at: "2025-08-04T11:00:00.000Z"
});
Output
{
access_grant_id: '6d74aefc-5712-4a8b-82c1-73a51ae60b87',
user_identity_id: '8cc2633a-54ca-455a-8a2b-77e6a1fc4fee',
starts_at: '2025-08-01T15:00:00.000Z',
ends_at: '2025-08-04T11:00:00.000Z'
instant_key_url: 'https://ik.seam.co/ABCXYZ',
requested_access_methods: [
{
display_name: 'Plastic card',
mode: 'card',
// Note the access_method_id of the created card access method.
created_access_method_ids: ["5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f"],
created_at: '2025-06-16T16:54:19.946606Z'
}
],
...
}
Code
# Identify the IDs of the entrances to which
# you want to grant access.
entrances = seam.acs.entrances.list(
# Use the access system ID that you copied in the previous step.
acs_system_id: acs_system_id
)
# Create the Access Grant.
access_grant = seam.access_grants.create(
# Create a new user identity to represent your user.
user_identity: {
full_name: "Jane Doe",
email_address: "[email protected]",
phone_number: "+15555551000"
},
# Specify the IDs of the entrances to which you want to grant access.
acs_entrance_ids: [
entrances[0].acs_entrance_id,
entrances[1].acs_entrance_id
],
# Specify that you want to issue a card access method.
requested_access_methods: [
{ mode: "card" }
],
# Specify the access schedule.
starts_at: "2025-08-01T15:00:00.000Z",
ends_at: "2025-08-04T11:00:00.000Z"
)
Output
<Seam::Resources::AccessGrant:0x005f0
access_grant_id="6d74aefc-5712-4a8b-82c1-73a51ae60b87"
user_identity_id="8cc2633a-54ca-455a-8a2b-77e6a1fc4fee"
starts_at=2025-08-01 15:00:00 UTC
ends_at=2025-08-04 11:00:00 UTC
instant_key_url="https://ik.seam.co/ABCXYZ"
requested_access_methods=[
{
"display_name"=>"Plastic card",
"mode"=>"card",
# Note the access_method_id of the created card access method.
"created_access_method_ids"=>["5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f"],
"created_at"=>"2025-06-16T16:54:19.946606Z"
}
]
...
>
Code
// Identify the IDs of the entrances to which
// you want to grant access.
$entrances = $seam->acs->entrances->list(
// Use the access system ID that you copied in the previous step.
acs_system_id: $acs_system_id
);
// Create the Access Grant.
$access_grant = $seam->access_grants->create(
// Create a new user identity to represent your user.
user_identity: [
"full_name" => "Jane Doe",
"email_address" => "[email protected]",
"phone_number" => "+15555551000"
],
// Specify the IDs of the entrances to which you want to grant access.
acs_entrance_ids: [
$entrances[0]->acs_entrance_id,
$entrances[1]->acs_entrance_id
],
// Specify that you want to issue a card access method.
requested_access_methods: [
["mode" => "card"]
],
// Specify the access schedule.
starts_at: '2025-08-01T15:00:00.000Z',
ends_at: '2025-08-04T11:00:00.000Z'
);
Output
{
"access_grant_id":"6d74aefc-5712-4a8b-82c1-73a51ae60b87",
"user_identity_id":"8cc2633a-54ca-455a-8a2b-77e6a1fc4fee",
"starts_at":"2025-08-01T15:00:00.000Z",
"ends_at":"2025-08-04T11:00:00.000Z"
"instant_key_url":"https://ik.seam.co/ABCXYZ",
"requested_access_methods":[
{
"display_name":"Plastic Card",
"mode":"card",
// Note the access_method_id of the created card access method.
"created_access_method_ids":["5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f"],
"created_at":"2025-06-16T16:54:19.946606Z"
}
],
...
}
Code
// Coming soon!
Output
// Coming soon!
2. Retrieve Encoders
There may be multiple encoders at a location, so it’s important to select the right one to encode the credential.
This example shows how to retrieve all encoders in a building connected to a single access system. Once you've identified the encoder you'd like to use, save the acs_encoder_id of the chosen encoder for the next step.
Once you issue a request to encode the access method onto the card, it is important to confirm that the encoding process completes successfully. You can use polling or a webhook.
Confirm Successful Encoding by Polling
When you make an /access_methods/encode request, Seam returns an action attempt. To confirm that the card encoding was successful, you can poll this action attempt, until its status becomes success.
It is also useful to note that Seam assigns values to various card-related properties on the access method when the encoder has finished encoding the card. For example, access_method.issued_at receives a value. You can retrieve the access method to view these properties.
To confirm successful encoding, you can use a webhook to listen for an access_method.issued event that contains the access_method_id in the payload. If you are re-encoding a card, listen for access_method.reissued instead of access_method.issued.