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
curlandjq.
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.
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.
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:
{
"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.
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.
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.
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.
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.
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:
{
"items": [
{
"credentialId": "<credentialId>",
"credentialType": "cardCredential",
"assignedTo": "<identityId>"
}
],
"limit": 10,
"offset": 0,
"total": 1
}
{
"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.
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 |
|---|---|---|
|
|
Token was issued for the wrong audience, expired, or was not sent. |
Recheck the environment-specific authentication handoff. |
|
|
The caller lacks a create, assign, or verification permission. |
Compare the role to the required permission list at the beginning of the Quickstart section. |
|
|
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. |
|
|
An external ID or assignment conflicts with an existing record. |
Reconcile using the external ID and existing assignment before issuing another create. |
Continue
-
Learn about the identity lifecycle in Identities.
-
Add PACS discovery and mappings in PACS integration before expecting physical-access provisioning.
-
Learn how card type mappings control validation in Credentials and card types.
-
Follow updates and concurrency before adding update or deletion behavior.