Skip to content

Building a secure integration

For developers

An integration with CardOffice moves personal information and photographs between systems. Whatever protects that information inside CardOffice stops at your end of the connection, so your integration needs the same care. Work through this list before you go live.

  • One key per system, and per environment. A key for your student system, another for a photo migration, separate keys for test and production. When one has to be revoked, nothing else stops.
  • Grant the fewest scopes that work. A system that only reads the roster needs card_holders:read, nothing more. See Scopes.
  • Give every key an expiry, and rotate before it arrives. See Rotating and revoking.
  • Keep keys in a secret store, never in source code, configuration files checked into a repository, scripts on shared drives, or anything that runs in a browser or on a phone.
  • Treat photos:attest as privileged. A key with it can put photos onto cards without anyone reviewing them. Use it only for a one-off load from a trusted archive, then revoke it.
  • Call the API from your servers only. The API is not designed to be called from a browser, and a key in a web page is a published key.
  • Verify every signature before acting on a delivery, using the raw request body. See Verifying signatures.
  • Reject old timestamps, so a captured delivery cannot be replayed later.
  • Use a different endpoint, with its own secret, for each environment.
  • Treat a delivery as a notification, not as a source of truth. When a decision matters, such as disabling door access, read the current state back from the API first.

Send CardOffice only what the card and its processes use. A card does not need a date of birth, a home address, a national identity number or health information, so do not put them in customFields or notes. Information that was never sent cannot leak.

The same applies in the other direction. Store only what your system needs from CardOffice, and do not keep copies of photos longer than you need them.

  • Upload links and download links expire quickly by design. Use them straight away and do not store them.
  • Upload photos only for the person they show. A photo becomes the face on an identity credential.
  • If your system keeps photos it has downloaded, protect and expire them as you would in CardOffice.

Your integration’s logs are a copy of what passed through it. Do not log API keys, webhook secrets, Authorization headers, upload or download links, or photo bytes. Avoid logging full card holder records; an ID and a status is usually enough to debug.

Decide which system is the source of truth for whether someone is still active, and send that change to CardOffice promptly, by SCIM or the API. A person marked inactive stops passing verification at once. If a door access system also needs to know, have it act on the card_holder.status_changed webhook. Revoking a card they already hold is a separate step. See Deactivating is not revoking.

  • Keys are scoped, expiring and stored in a secret store
  • Test and production use different keys and webhook endpoints
  • Webhook signatures and timestamps are checked
  • Nothing the card does not use is sent to CardOffice
  • Logs contain no keys, links or photos
  • Someone owns rotating the keys and knows how to revoke one in a hurry

© 2026 Secure ID Ltd. All rights reserved.Documentation licence and terms