Skip to main content
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:
  1. Create an Access Grant that includes a request for a card access method and identify the ID of the resulting access method. See Create an Access Grant.
  2. 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.
  3. 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.
  4. Confirm that the card was encoded successfully using polling or a webhook.
    See Confirm Successful Encoding. Also, see a list of common encoding errors.
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. For more details about creating Access Grants, see Creating an Access Grant Using Entrances and Creating an Access Grant Using Spaces.
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: 'jane.doe@example.com',
    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'
    }
  ],
  ...
}

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.
Code:
await seam.acs.encoders.list({
  acs_system_ids: [buildingSystemId],
})
Output:
[
  {
    acs_encoder_id: '33333333-4444-5555-6666-777777777777',
    display_name: 'Front Desk',
    ...
  },
  ...
]

3. Encode the Card

Use /acs/encoders/encode_credential to start the card writing procedure that stores the access method data on the plastic key card.
Code:
const encodingActionAttempt = await seam.accessMethods.encode({
  access_method_id: accessMethodId,
  acs_encoder_id: encoder.acs_encoder_id,
})
Output:
{
  status: 'pending',
  action_attempt_id: '11111111-2222-3333-4444-555555555555",
  action_type: 'ENCODE_ACCESS_METHOD',
  result: null,
  error: null
}

4. Confirm Successful Encoding

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.
Code:
await seam.actionAttempts.get({
  action_attempt_id: encodingActionAttempt.action_attempt_id,
})
Output:
{
  status: 'success',
  action_attempt_id: '11111111-2222-3333-4444-555555555555",
  action_type: 'ENCODE_ACCESS_METHOD',
  result: {
    access_method_id: "5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f',
    card_number: '1234abc',
    is_issued: true,
    issued_at: '2025-06-16T16:56:00.000000Z',
    ...
  },
  error: null
}

View Access Method Properties

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.
Code
await seam.accessMethods.get({
  access_method_id: accessMethodId,
})
Output
{
  "access_method_id": "5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f",
  "issued_at": "2025-06-16T16:56:00.000000Z",
  ...
}

Confirm Successful Encoding by Using a Webhook

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.
{
  "event_id": "22222222-3333-4444-5555-666666666666",
  "event_description": "An access method was issued.",
  "event_type": "access_method.issued",
  "workspace_id": "00000000-0000-0000-0000-000000000000",
  "created_at": "2025-06-16T16:56:00.000000Z",
  "occurred_at": "2025-06-16T16:56:00.000000Z",
  "acs_system_id": "11111111-1111-1111-1111-111111111111",
  "access_method_id": "5f4e3d2c-1b0a-9f8e-7d6c-5b4a3c2d1e0f",
  "connected_account_id": "11111111-1111-1111-1111-222222222222"
}

Common Encoding Errors

ErrorDescription
no_card_on_encoderNo card was placed on the encoder.
incompatible_card_formatA card with an incompatible card format was placed on the encoder.
uncategorized_errorAny other encoding error.