Use a visitor and visit together to model temporary physical access. A visitor is a reusable person record; a visit is the time-bound or operational context, such as a meeting, interview, or delivery. A visit participant joins one visitor to one visit.
Visitor ── assigned to ──> Visit
│
visit-scoped credentials + visit access units
Creating a visitor or visit, and assigning a participant, does not itself grant access. Access is represented by a participant with a suitable visit-scoped credential and an eligible access unit assigned to the visit.
Visitor admission example: create, check in, verify, and check out
This non-production example creates all records needed for a visitor admission flow. It creates a card type and access unit eligible for visitors, but no PACS mappings; it verifies ICAP's intended state only and does not make a physical badge usable at a door.
Use an existing non-production tenant approved for fictional sample records, with PACS integration disabled. These payloads assume no required custom fields for visitors, visits, or card credentials; include any required values before running the example.
The role needs the endpoint permissions for the operations below: VisitorCreate, VisitCreate, VisitAssignVisitor, CardTypeCreate, AccessUnitCreate, CardCredentialCreate, VisitorCredentialAssign, VisitAccessUnitAssign, VisitCheckInVisitor, VisitCheckOutVisitor, VisitListVisitors, VisitorCredentialList, VisitorList, and VisitAccessUnitList.
set -euo pipefail
BASE_URL='https://<your-icap-host>/icap/v1/tenant/<tenantId>'
TOKEN='<access-token>'
RUN_ID="${RUN_ID:-visitor-demo-$(date -u +%Y%m%dT%H%M%SZ)}"
Create the visitor and visit. start, end, and hostId are optional descriptive fields; they do not change attendance automatically.
VISITOR_RESPONSE="$(curl --fail-with-body --silent --show-error \
--request POST "$BASE_URL/visitor" \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "{
\"externalIds\": { \"demo\": \"$RUN_ID-visitor\" },
\"firstName\": \"Grace\",
\"lastName\": \"Hopper\"
}")"
VISITOR_ID="$(jq --exit-status --raw-output '.id' <<<"$VISITOR_RESPONSE")"
VISIT_RESPONSE="$(curl --fail-with-body --silent --show-error \
--request POST "$BASE_URL/visit" \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "{
\"externalIds\": { \"demo\": \"$RUN_ID-visit\" },
\"name\": \"Visitor demonstration $RUN_ID\"
}")"
VISIT_ID="$(jq --exit-status --raw-output '.id' <<<"$VISIT_RESPONSE")"
Create a visitor-eligible card type, access unit, and card credential. In a PACS-backed workflow, discover the PACS formats and access units before adding mappings.
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\": \"Visitor card $RUN_ID\", \"eligibleFor\": \"visitor\" }")"
CARD_TYPE_ID="$(jq --exit-status --raw-output '.id' <<<"$CARD_TYPE_RESPONSE")"
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\": \"Visitor reception $RUN_ID\", \"eligibleFor\": \"visitor\" }")"
ACCESS_UNIT_ID="$(jq --exit-status --raw-output '.id' <<<"$ACCESS_UNIT_RESPONSE")"
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")"
Assign the visitor to the visit, then assign the credential and access unit. A participant starts in planned status.
curl --fail-with-body --silent --show-error \
--request POST "$BASE_URL/visit/$VISIT_ID/visitor" \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "{ \"visitorId\": \"$VISITOR_ID\" }"
curl --fail-with-body --silent --show-error \
--request POST "$BASE_URL/visit/$VISIT_ID/visitor/$VISITOR_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/visit/$VISIT_ID/accessUnit" \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "{ \"accessUnitId\": \"$ACCESS_UNIT_ID\" }"
Check in, verify the participant and assignments, then check out. The two attendance calls have no request body in the current contract.
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer $TOKEN" \
"$BASE_URL/visit/$VISIT_ID/visitor/$VISITOR_ID/checkIn"
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $TOKEN" \
"$BASE_URL/visit/$VISIT_ID/visitor?limit=10"
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $TOKEN" \
"$BASE_URL/visit/$VISIT_ID/visitor/$VISITOR_ID/credential?limit=10"
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $TOKEN" \
"$BASE_URL/visit/$VISIT_ID/accessUnit?limit=10"
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer $TOKEN" \
"$BASE_URL/visit/$VISIT_ID/visitor/$VISITOR_ID/checkOut"
The check-in response is a participant record like:
{
"visitorId": "<visitorId>",
"visitId": "<visitId>",
"status": "checkedIn",
"checkedInAt": "2026-09-07T10:15:00Z"
}
If check-in or check-out fails, list the visit participants and reconcile with the front-desk record before retrying. See PACS integration for downstream provisioning and transaction recovery; only a completed PACS transaction confirms physical-access availability.
Remove the sample records
Checkout updates attendance; it does not remove the visitor, visit, credential, access unit, or card type. Remove only the IDs created by this example.
Use this order:
-
Remove the participant's credential assignment.
-
Remove the visit's access-unit assignment.
-
Remove the visitor-to-visit assignment.
-
Remove the card credential.
-
Remove the visit and visitor.
-
Remove the access unit, then the card type.
Assignment removals do not require If-Match. For each resource removal, read its current version immediately before sending DELETE with that version. Use the cleanup helpers from Quickstart: a verified first access assignment with these paths, in order:
delete_assignment "$BASE_URL/visit/$VISIT_ID/visitor/$VISITOR_ID/credential/$CREDENTIAL_ID"
delete_assignment "$BASE_URL/visit/$VISIT_ID/accessUnit/$ACCESS_UNIT_ID"
delete_assignment "$BASE_URL/visit/$VISIT_ID/visitor/$VISITOR_ID"
delete_versioned "$BASE_URL/cardCredential/$CREDENTIAL_ID"
delete_versioned "$BASE_URL/visit/$VISIT_ID"
delete_versioned "$BASE_URL/visitor/$VISITOR_ID"
delete_versioned "$BASE_URL/accessUnit/$ACCESS_UNIT_ID"
delete_versioned "$BASE_URL/cardType/$CARD_TYPE_ID"
In addition to the walkthrough permissions, cleanup requires:
VisitorCredentialRemove
VisitAccessUnitRemove
VisitRemoveVisitor
CardCredentialGet
CardCredentialRemove
VisitGet
VisitRemove
VisitorGet
VisitorRemove
AccessUnitGet
AccessUnitRemove
CardTypeGet
CardTypeRemove
This sample cleanup is not a personal-data purge. For production retention or downstream removal, follow Privacy and lifecycle and PACS integration.
Participants and attendance
One active assignment exists for a given visitor/visit pair. The API controls status transitions:
planned → checkedIn → checkedOut
Use the visit-participant endpoints under /visit/{visitId}/visitor. List participants by visit, or list visits for a visitor. You can filter both lists by participant status.
Data and identifiers
Visitors use the same essential person fields as identities: client-supplied externalIds, required first and last names, optional contact data, photo, and custom fields. Visitors and visits each have separate external-ID namespaces, so a value can appear once in each kind of record.
start, end, type, and hostId describe the visit but do not create authorization rules. A hostId must identify an existing identity; it does not make the host a participant.
Visitor photos are excluded unless includePhoto=true. For field-level schemas, use the Identity section of the API reference.
Credentials and access units
Credentials for a visitor are assigned in the visit context, not directly to /visitor. Card types and access units must be eligible for visitors or for any. A credential has only one active assignment.
See credentials and card types and access units before building an admission flow. For endpoint details, see Visitors and visits.
Lifecycle and privacy
Visitor and visit removal uses soft deletion. Visitors additionally support purge; visits do not. Updates, resource removals, and visitor purge require the relevant current version in If-Match.
Removing a participant also removes its credential assignments for that visit. Removing a visit removes its participant assignments, access-unit assignments, and visit-scoped credential assignments. Removing a visitor removes its participant and credential assignments. These operations do not physically erase the referenced credential records.
Verify the downstream result when removing visitors or visits from a PACS-backed workflow. See PACS integration for current cleanup limitations and Privacy and lifecycle for purge behavior.