User Documentation

Quickstart: a verified first access assignment

This walkthrough creates the configuration and relationships needed for ICAP to record one identity's card credential and access-unit assignment. It is intended for a non-production tenant approved for sample data. It does not configure a PACS or make a physical badge usable at a door.

Before you run the example

Use an existing non-production tenant approved for fictional sample records. The sample assumes PACS integration is disabled and that the tenant has no required custom fields for identities or card credentials. If required fields are configured, include valid values in each affected create request before continuing.

To obtain the authentication token, follow the Authentication and authorization manual and its Authentication and authorization.

Run the commands in Bash with curl and jq available. Use the base URL, tenant ID, and token for your environment. If you need access, contact Customer Service.

The create and verification steps require the following permitions:

AccessUnitCreate
CardCredentialCreate
CardTypeCreate
IdentityAccessUnitAssign
IdentityAccessUnitList
IdentityCreate
IdentityCredentialAssign
IdentityCredentialList
IdentityGet
IdentityList

Cleanup requires the additional permissions listed in “Remove the sample objects.”

Keep the administrator role separate from an application role. Grant only the permissions required by the workflow, and use a distinct principal for automation where your identity policy permits it.

Set up the request context

  • Replace all placeholders.

  • The commands save returned IDs in shell variables so that no ID is guessed or copied from a sample response.

  • They require curl and jq.

Bash
set -euo pipefail

BASE_URL='https://<your-icap-host>/icap/v1/tenant/<tenantId>'
TOKEN='<access-token>'
RUN_ID="${RUN_ID:-quickstart-$(date -u +%Y%m%dT%H%M%SZ)}"

Use a distinct RUN_ID for a new sample run. Retain the returned resource IDs if you need to continue or clean up later. Reusing RUN_ID does not restore shell variables or make create requests idempotent. After an interrupted or uncertain request, find and verify the existing resource before repeating a create operation.

Every create and verification curl command below uses --fail-with-body: a non-success response is shown and the command fails instead of silently producing an empty ID.


0. Verify access before creating data

This request requires IdentityList permition. A successful response, including an empty collection, verifies the tenant and authorization for this read operation.

Bash
curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $TOKEN" \
  "$BASE_URL/identity?offset=0&limit=1&includePhoto=false"

Resolve authentication, tenant, or permission errors before continuing. The permission-catalog endpoint is useful for role configuration but does not verify tenant existence or your role assignment.

1. Create a card type

Card credentials require a card type. This basic card type is eligible for identities and has no PACS format mapping; configure mappings only after PACS discovery.

Bash
CARD_TYPE_RESPONSE="$(curl --fail-with-body --silent --show-error \
  --request POST "$BASE_URL/cardType" \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "{
    \"name\": \"Quickstart identity card $RUN_ID\",
    \"description\": \"Non-production sample card type\",
    \"eligibleFor\": \"identity\"
  }")"
CARD_TYPE_ID="$(jq --exit-status --raw-output '.id' <<<"$CARD_TYPE_RESPONSE")"

The response contains an ICAP-generated ID and version. The concrete IDs and audit fields will differ in your tenant:

JSON
{
  "id": "<cardTypeId>",
  "tenantId": "<tenantId>",
  "version": 1,
  "name": "Quickstart identity card <runId>",
  "eligibleFor": "identity"
}

2. Create an access unit

An access unit is the business access target to assign. With no PACS mapping it records intended ICAP state only.

Bash
ACCESS_UNIT_RESPONSE="$(curl --fail-with-body --silent --show-error \
  --request POST "$BASE_URL/accessUnit" \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "{
    \"name\": \"Quickstart demonstration area $RUN_ID\",
    \"eligibleFor\": \"identity\"
  }")"
ACCESS_UNIT_ID="$(jq --exit-status --raw-output '.id' <<<"$ACCESS_UNIT_RESPONSE")"

3. Create an identity

externalIds are client-owned correlation values. Choose a value that is unique in this test tenant; retaining it lets you reconcile a timeout or retry without creating another person.

Bash
IDENTITY_RESPONSE="$(curl --fail-with-body --silent --show-error \
  --request POST "$BASE_URL/identity" \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "{
    \"externalIds\": { \"demo\": \"$RUN_ID-ada\" },
    \"firstName\": \"Ada\",
    \"lastName\": \"Lovelace\",
    \"emails\": [{ \"value\": \"ada@example.example\", \"type\": \"work\", \"primary\": true }]
  }")"
IDENTITY_ID="$(jq --exit-status --raw-output '.id' <<<"$IDENTITY_RESPONSE")"

4. Create a card credential

The current contract requires a cardType; card ID, facility ID, and card code are optional and their constraints come from the selected card type. This non-PACS example uses an external ID as its correlation value rather than inventing a PACS card number.

Bash
CARD_ID="$(( RANDOM % 99999 + 1 ))"
FACILITY_ID="$(( RANDOM % 99 + 1 ))"
CREDENTIAL_RESPONSE="$(curl --fail-with-body --silent --show-error \
  --request POST "$BASE_URL/cardCredential" \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "{
    \"externalIds\": { \"demo\": \"$RUN_ID-card\" },
    \"cardType\": \"$CARD_TYPE_ID\",
    \"cardId\": $CARD_ID,
    \"facilityId\": $FACILITY_ID
  }")"
CREDENTIAL_ID="$(jq --exit-status --raw-output '.id' <<<"$CREDENTIAL_RESPONSE")"

5. Assign the credential and access unit

These two assignments express the intended credential and access state in ICAP.

Bash
curl --fail-with-body --silent --show-error \
  --request POST "$BASE_URL/identity/$IDENTITY_ID/credential" \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "{
    \"credentialId\": \"$CREDENTIAL_ID\",
    \"credentialType\": \"cardCredential\"
  }"

curl --fail-with-body --silent --show-error \
  --request POST "$BASE_URL/identity/$IDENTITY_ID/accessUnit" \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "{ \"accessUnitId\": \"$ACCESS_UNIT_ID\" }"

6. Verify the result with reads

Do not treat the successful POST responses as the only evidence of success. Read the identity and both assignment collections, then check that the returned IDs match the IDs saved above.

Bash
curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $TOKEN" \
  "$BASE_URL/identity/$IDENTITY_ID"

curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $TOKEN" \
  "$BASE_URL/identity/$IDENTITY_ID/credential?limit=10"

curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $TOKEN" \
  "$BASE_URL/identity/$IDENTITY_ID/accessUnit?limit=10"

The last two responses are paged envelopes. Their important values look like this:

JSON
{
  "items": [
    {
      "credentialId": "<credentialId>",
      "credentialType": "cardCredential",
      "assignedTo": "<identityId>"
    }
  ],
  "limit": 10,
  "offset": 0,
  "total": 1
}
JSON
{
  "items": [
    { "accessUnitId": "<accessUnitId>" }
  ],
  "limit": 10,
  "offset": 0,
  "total": 1
}

If a PACS is connected later, these reads still verify only ICAP's intended state. Also monitor PACS transactions before treating the card or access unit as available in a physical system.

7. Remove the sample objects

Remove only the resources whose IDs were created in this run and only in a tenant approved for cleanup. This routine removes assignments first, then follows the creation dependencies: card credential, identity, access unit, and card type. It is safe to rerun after a partial cleanup: an already-absent assignment or resource is treated as complete.

In addition to the quickstart permissions, the cleanup caller needs:

AccessUnitGet
AccessUnitRemove
CardCredentialGet
CardCredentialRemove
CardTypeGet
CardTypeRemove
IdentityAccessUnitRemove
IdentityCredentialRemove
IdentityRemove

The assignment-removal endpoints do not require If-Match. Every resource removal below reads its current version first and supplies that value in If-Match.

Bash
delete_assignment() {
  local url="$1"
  local status
  status="$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' \
    --request DELETE \
    --header "Authorization: Bearer $TOKEN" \
    "$url")"
  case "$status" in
    204|404) ;;
    *) echo "Could not remove assignment: $url (HTTP $status)" >&2; return 1 ;;
  esac
}

delete_versioned() {
  local url="$1"
  local response body status version
  response="$(curl --silent --show-error --write-out $'\n%{http_code}' \
    --header "Authorization: Bearer $TOKEN" \
    "$url")"
  status="${response##*$'\n'}"
  body="${response%$'\n'*}"
  case "$status" in
    200) version="$(jq --exit-status --raw-output '.version' <<<"$body")" ;;
    404) return 0 ;;
    *) echo "Could not read resource: $url (HTTP $status)" >&2; return 1 ;;
  esac
  status="$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' \
    --request DELETE \
    --header "Authorization: Bearer $TOKEN" \
    --header "If-Match: $version" \
    "$url")"
  case "$status" in
    204|404) ;;
    *) echo "Could not remove resource: $url (HTTP $status)" >&2; return 1 ;;
  esac
}

delete_assignment "$BASE_URL/identity/$IDENTITY_ID/credential/$CREDENTIAL_ID"
delete_assignment "$BASE_URL/identity/$IDENTITY_ID/accessUnit/$ACCESS_UNIT_ID"

delete_versioned "$BASE_URL/cardCredential/$CREDENTIAL_ID"
delete_versioned "$BASE_URL/identity/$IDENTITY_ID"
delete_versioned "$BASE_URL/accessUnit/$ACCESS_UNIT_ID"
delete_versioned "$BASE_URL/cardType/$CARD_TYPE_ID"

Successful resource removal is a soft delete, not a general-purpose physical deletion. The quickstart uses fictional personal data; if a real person was created by mistake, follow the reviewed privacy and lifecycle process rather than treating this cleanup as a privacy purge.

Common first-run failures

Response or symptom

Likely cause

Fix

401 Unauthorized

Token was issued for the wrong audience, expired, or was not sent.

Recheck the environment-specific authentication handoff.

403 Forbidden

The caller lacks a create, assign, or verification permission.

Compare the role to the required permission list at the beginning of the Quickstart section.

400 Bad Request when creating a credential

The selected card type has constraints that the request does not meet.

Inspect the card type and use an allowed card ID, facility ID, or code; do not guess PACS format values.

409 Conflict after a timeout or retry

An external ID or assignment conflicts with an existing record.

Reconcile using the external ID and existing assignment before issuing another create.

Continue