---
title: Implementation Guide
slug: frameworks/zw2z-aci-i
createdAt: 2026-05-26T06:22:14.000Z
---

# Credential Lifecycle Flows

This section describes how each lifecycle operation should behave from an integration-implementation perspective.

## Lifecycle Summary

| Operation   | Trigger in Sine                                    | Endpoint            | Runtime expectation                      |
| ----------- | -------------------------------------------------- | ------------------- | ---------------------------------------- |
| Verify      | Integration setup and verification actions         | `POST /verify`      | Fast validation of auth and connectivity |
| Provision   | Visitor check-in (or configured pre-check-in flow) | `POST /provision`   | Synchronous and blocking                 |
| Activate    | Post-check-in activation stage                     | `POST /activate`    | Asynchronous and non-blocking            |
| Deprovision | Check-out, expiry, or callback                     | `POST /deprovision` | Asynchronous and non-blocking            |

## Provisioning

Provision is the critical gating call in the ACI lifecycle. If Provision fails, Sine cannot complete normal credential issuance for that visit.

### Security principle: Credentials must be provisioned inactive

Credentials provisioned to your ACS **must not grant access until explicitly activated**. This enforces a critical security boundary:

- A visitor checks in and is assigned a credential.
- Sine performs approval checks: notifying and waiting for confirmation from the visitor's host (the employee or staff member they are booked to meet), applying any site security validations, and running background checks if configured.
- Only after Sine confirms approval does it call Activate.
- Activate is the authoritative gate for when physical access is actually granted.

If your ACS cannot represent inactive/disabled credentials, your integration must track activation state separately and reject reader/turnstile requests until Activate has been called. **Do not allow access based on provisioned state alone.**

### Trigger and intent

- Called when Sine needs to create or attach a credential for a specific visit.
- Must return correlation identifiers used by later Activate and Deprovision calls.

### Integration responsibilities

- Validate authentication and request payload.
- Create or attach the ACS holder/credential record.
- Return `PROVISIONED` and the required `data` object.
- Persist enough internal mapping to support idempotent retries.

### Implementation notes

- Treat `badgeNumber`/`barcode` as possibly pre-existing values.
- For physical and deactivate-only capable integrations, handle reuse safely (match-existing, upsert, or explicit conflict response).
- For digital credentials, generate non-guessable credential values.

### Minimum successful response shape

```json
{
    "code": "PROVISIONED",
    "data": {
        "barcode": "<credential-value>",
        "accessControlVisitId": "<acs-holder-id>",
        "accessControlLocationId": "<optional-acs-location-id>"
    }
}
```

### Provision flow checkpoints

1. Authenticate request and validate required fields.
2. Resolve access groups and card/credential handling mode.
3. Create or link ACS holder and credential.
4. Return ACS correlation IDs and credential identifier.
5. Log with visit and correlation IDs (without leaking secrets).

## Activation

Activation enables access for an already-provisioned credential.

### Why Activation exists: The approval gate

Provisioning creates a credential, but Activation is where Sine grants permission to use it. This two-step design allows Sine to:

- Notify the visitor's host (the employee they are booked to meet) and wait for their approval if required.
- Apply any site security policies or screening checks.
- Cancel the access grant if approval is not given - in this case, no Activate call is made.
- Enforce a clear audit trail of who approved access, and when.

Your endpoint must respect this: **a provisioned but non-activated credential should not grant physical access regardless of whether it exists in the ACS**. Activation is the commitment to access.

### Trigger and intent

- Called after Provision when Sine transitions the visit into an active state.
- Uses previously issued identifiers (`accessControlVisitId`, `badgeNumber`/`barcode`, and optional location/credential metadata).

### Integration responsibilities

- Resolve the target holder/credential in ACS using supplied identifiers.
- Enable access rights for the visit window and configured access groups.
- Return `ACTIVATED` for successful or idempotently-already-active outcomes.

### Operational behaviour

- Activation is asynchronous from Sine's lifecycle perspective.
- Failures are operationally significant, but do not retroactively block a completed check-in.
- Your endpoint should remain idempotent for duplicate activation calls.

### Activation flow checkpoints

1. Validate identity and correlation fields.
2. Resolve holder/credential relation in ACS.
3. Apply activation (or no-op if already active).
4. Return success and structured logs.

## Deprovisioning

Deprovision removes or disables access at the end of the visit lifecycle.

### Trigger and intent

- Called on check-out, expiry, or callback-driven checkout.
- Can be invoked in standard mode or deactivate-only mode.

### Modes

| Mode            | Request signal                     | Expected behaviour                                     |
| --------------- | ---------------------------------- | ------------------------------------------------------ |
| Standard        | `deactivateOnly` absent or `false` | Apply normal revoke/deprovision behaviour              |
| Deactivate-only | `deactivateOnly=true`              | Disable access while preserving reusable/audit records |

### Integration responsibilities

- Handle both modes independent of credential type.
- Treat repeated deprovision calls as idempotent.
- If records are already inactive/removed, return success rather than hard failure where safe.

### Deprovision flow checkpoints

1. Validate auth and correlation identifiers.
2. Determine mode from query flag (`deactivateOnly`).
3. Execute revoke/deactivate logic in ACS.
4. Return `DEPROVISIONED` and emit traceable logs.

## Error and Retry Behaviour

Implementations should separate permanent request errors from transient operational failures so Sine can react correctly.

### Recommended status mapping

| Scenario                                                 | HTTP status                                    | Response guidance                         |
| -------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------- |
| Invalid payload or field constraints                     | `400`                                          | Return validation details in `message`    |
| Missing or invalid API key                               | `401`                                          | Return auth failure message               |
| Auth valid but operation not permitted                   | `403`                                          | Return permission/context error           |
| Identifier conflict or duplicate that cannot be resolved | `409` (or `400` if no conflict status support) | Explain conflict and expected remediation |
| Downstream ACS unavailable/timeout                       | `500` / `502` / `503` / `504`                  | Return transient failure message          |

### Retry design guidance

- Provision: treat as blocking and fail-fast on non-recoverable errors.
- Activate/Deprovision: design for retries and duplicate delivery.
- Use idempotent handlers so repeated requests do not create duplicate holders, duplicate cards, or inconsistent access state.
- Reuse stable keys (`visitId`, `accessControlVisitId`, `badgeNumber`) to deduplicate safely.

### Idempotency rules of thumb

1. "Already exists" during Provision should be handled deterministically, not as an unclassified 500.
2. "Already active" during Activate should return success-equivalent behaviour.
3. "Already deprovisioned/deactivated" during Deprovision should return success-equivalent behaviour.
4. Never rely on a single delivery attempt for Activate/Deprovision.

### Structured error response

Use this shape for non-success responses:

```json
{
    "code": "REQUEST_FAILED_ERROR",
    "message": "Human-readable failure description"
}
```

Keep messages actionable for operators - include what failed, where it failed, and whether a retry is likely to succeed.

# Endpoint Implementation Reference

This section describes the full request and response contract for each endpoint your integration must expose. All lifecycle endpoints use `POST` unless otherwise stated.

## Common Request Headers

All lifecycle requests from Sine include:

| Header          | Value                   |
| --------------- | ----------------------- |
| `Authorization` | Your configured API key |
| `Content-Type`  | `application/json`      |

Validate the `Authorization` header on every request. Return `401` if the key is missing or does not match.

***

## Overview of Required Endpoints

| Endpoint         | Method | Required | Purpose                                                  |
| ---------------- | ------ | -------- | -------------------------------------------------------- |
| `/verify`        | `POST` | Yes      | Validate API key and connectivity during setup           |
| `/provision`     | `POST` | Yes      | Create or attach a credential for a visit                |
| `/activate`      | `POST` | Yes      | Enable access for a provisioned credential               |
| `/deprovision`   | `POST` | Yes      | Revoke or deactivate access at end of visit              |
| `/access-groups` | `GET`  | Optional | Return available access groups for Sine configuration UI |

The paths shown above are illustrative. Your integration can use any URL paths - the actual endpoints are registered in your integration configuration. See [Section 6](#) for setup details.

***

## Endpoint Specifications

### Verify

**Purpose:** Confirms that Sine can reach your endpoint and that the API key is valid. Called during integration setup and when administrators test connectivity.

**Request body:**

```json
{
  "externalId": "your-site-reference"
}
```

| Field        | Type   | Required | Description                                                                      |
| ------------ | ------ | -------- | -------------------------------------------------------------------------------- |
| `externalId` | string | No       | A site-specific reference string configured in Sine. Present only if configured. |

**Success response** (`200 OK`):

```json
{
  "code": "SUCCESS"
}
```

**Notes:**

- Keep this endpoint lightweight - it is used as a connectivity check during setup.
- No ACS operations are expected. Validate the API key and return success.

***

### Provision

**Purpose:** Creates or attaches a credential in your ACS for a specific visit. The credential must be created in an **inactive or disabled state** - access is not enabled until Activate is called.

**Request body:**

```json
{
  "visitId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "siteId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "externalId": "your-site-reference",
  "credentialType": "digital",
  "validFrom": "2026-05-25T09:00:00.000Z",
  "validTo": "2026-05-25T17:00:00.000Z",
  "visitor": {
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane.smith@example.com",
    "mobile": "+61400000000",
    "photoURL": "https://signed-url.example.com/photo.jpg"
  },
  "visitorType": {
    "id": "vt-001",
    "name": "Contractor"
  },
  "company": {
    "id": "co-001",
    "name": "ACME Corp"
  },
  "host": {
    "id": "h-001",
    "firstName": "Alex",
    "lastName": "Johnson",
    "email": "alex.johnson@yoursite.com"
  },
  "accessGroups": [
    "782b7f6e-ef97-4242-9c58-81cc0e3af04d"
  ],
  "checkoutUrl": "https://app.sine.co/callbacks/checkout/{jwt}?teamId=xyz"
}
```

**Request field reference:**

| Field               | Type             | Required | Description                                                                                                                                                                       |
| ------------------- | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `visitId`           | UUID v4          | Yes      | Unique Sine identifier for this visit. Use as your primary idempotency key.                                                                                                       |
| `siteId`            | UUID v4          | Yes      | Sine identifier for the site/location.                                                                                                                                            |
| `externalId`        | string           | No       | Integration-specific reference string configured in Sine. Max 256 characters.                                                                                                     |
| `credentialType`    | string           | No       | `digital`, `physical`, or `virtual`. See [Credential Types](<./Getting Started.mdx>)                                                                                              |
| `validFrom`         | string           | Yes      | ISO 8601 UTC. Start of the credential validity window.                                                                                                                            |
| `validTo`           | string           | Yes      | ISO 8601 UTC. End of the credential validity window.                                                                                                                              |
| `visitor`           | object           | Yes      | Visitor details.                                                                                                                                                                  |
| `visitor.firstName` | string           | Yes      | Visitor's first name.                                                                                                                                                             |
| `visitor.lastName`  | string           | Yes      | Visitor's last name.                                                                                                                                                              |
| `visitor.email`     | string           | No       | Visitor's email address.                                                                                                                                                          |
| `visitor.mobile`    | string           | No       | Visitor's phone number in E.164 format (e.g. `+61400000000`).                                                                                                                     |
| `visitor.photoURL`  | string           | No       | Signed, time-limited URL to the visitor's photo. Do not persist beyond operational need.                                                                                          |
| `visitorType`       | object           | No       | Visitor classification. Includes `id` and `name`.                                                                                                                                 |
| `company`           | object           | No       | Visitor's company. Includes `id` and `name`.                                                                                                                                      |
| `host`              | object           | No       | The employee the visitor is meeting. Includes `id`, `firstName`, `lastName`, `email`, `mobile`, `hostGroup`, `hostGroupName`.                                                     |
| `accessGroups`      | array of strings | No       | ACS access group IDs to apply to this credential. See [Access Groups](<./Getting Started.mdx>) .                                                                                  |
| `checkoutUrl`       | string           | No       | Callback URL your ACS should call when the visitor badges out at an exit reader. Includes an authentication token. See [Check-Out Flow - ACS Initiated](<./Getting Started.mdx>). |
| `badgeNumber`       | string           | No       | Pre-assigned card number. Present for `physical` credential flows. See [Check-In Flow - Physical Card Credential](<./Getting Started.mdx>) .                                      |
| `zoneId`            | string           | No       | Legacy field. Zone identifier when zone feature is enabled.                                                                                                                       |
| `zoneDetails`       | object           | No       | Legacy field. Zone `id`, `name`, and `externalId` when zone feature is enabled.                                                                                                   |

**Success response** (`201 Created`):

```json
{
  "code": "PROVISIONED",
  "data": {
    "barcode": "2345v34sdv",
    "accessControlVisitId": "1234",
    "accessControlLocationId": "6789"
  }
}
```

**Response field reference:**

| Field                          | Type   | Required | Description                                                                                                                       |
| ------------------------------ | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `code`                         | string | Yes      | Must be `PROVISIONED`.                                                                                                            |
| `data.barcode`                 | string | Yes      | The credential value (QR code data or card number). For digital credentials, must be unpredictable - see notes below.             |
| `data.accessControlVisitId`    | string | Yes      | Your ACS-side identifier for this visit/holder. Sine stores this and returns it in all subsequent Activate and Deprovision calls. |
| `data.accessControlLocationId` | string | No       | Optional ACS location partition key. Stored and echoed back by Sine in later calls.                                               |
| `data.destinationName`         | string | No       | Display label for the pass (e.g. floor or room name). Used on badge printing or QR display.                                       |
| `data.destinationId`           | string | No       | Identifier for the destination.                                                                                                   |

**Notes:**

- Provision is **synchronous and blocking**. Sine waits for the response before displaying or printing the credential. Slow or failed responses will delay or block check-in.
- The `barcode` returned here becomes the `badgeNumber` Sine sends in all subsequent Activate and Deprovision calls.
- For **digital credentials**, `barcode` must be non-sequential and non-guessable. Do not use card numbers, sequential integers, or visitor IDs.
- For **physical credentials**, Sine sends the pre-assigned card number as `badgeNumber` in the request. Echo it back as `barcode` in the response.
- `photoURL` values are signed and ephemeral. Do not store them beyond the current request context.

### Activate

**Purpose:** Enables access for a previously provisioned credential. Called after Sine has completed its approval checks for the visit.

**Request body:**

```json
{
  "visitId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "siteId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "externalId": "your-site-reference",
  "credentialType": "digital",
  "validFrom": "2026-05-25T09:00:00.000Z",
  "validTo": "2026-05-25T17:00:00.000Z",
  "accessControlVisitId": "1234",
  "accessControlLocationId": "6789",
  "badgeNumber": "2345v34sdv",
  "accessGroups": [
    "782b7f6e-ef97-4242-9c58-81cc0e3af04d"
  ]
}
```

**Request field reference:**

| Field                     | Type             | Required | Description                                                                          |
| ------------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------ |
| `visitId`                 | UUID v4          | Yes      | Visit identifier from Provision.                                                     |
| `siteId`                  | UUID v4          | Yes      | Sine site identifier.                                                                |
| `externalId`              | string           | No       | Integration reference string. Same value as Provision.                               |
| `credentialType`          | string           | No       | Credential type. Same value as Provision.                                            |
| `accessControlVisitId`    | string           | Yes      | The ACS identifier you returned in the Provision response.                           |
| `accessControlLocationId` | string           | No       | The ACS location identifier you returned in the Provision response, if applicable.   |
| `badgeNumber`             | string           | Yes      | The credential value (`barcode`) returned in the Provision response.                 |
| `accessGroups`            | array of strings | No       | Access group IDs to apply. May differ from Provision if updated by an administrator. |

**Success response** (`200 OK`):

```json
{
  "code": "ACTIVATED"
}
```

**Notes:**

- Activate is **asynchronous from Sine's perspective**. Sine does not hold the check-in flow open waiting for the outcome.
- Your endpoint must be idempotent. If the credential is already active, return `ACTIVATED` rather than an error.
- Use `accessControlVisitId` and/or `badgeNumber` to resolve the target credential. Do not rely solely on `visitId`.

### Deprovision

**Purpose:** Revokes or disables a credential at the end of the visit lifecycle. Standard behaviour removes the credential entirely. Deactivate-only mode preserves the record.

**Request body:**

```json
{
  "visitId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "siteId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "externalId": "your-site-reference",
  "credentialType": "digital",
  "validFrom": "2026-05-25T09:00:00.000Z",
  "validTo": "2026-05-25T17:00:00.000Z",
  "accessControlVisitId": "1234",
  "accessControlLocationId": "6789",
  "badgeNumber": "2345v34sdv",
  "accessGroups": [
    "782b7f6e-ef97-4242-9c58-81cc0e3af04d"
  ]
}
```

The request body fields are identical to Activate. The deactivate-only mode is signalled via a query parameter:

```javascript
POST /deprovision?deactivateOnly=true
```

| Parameter        | Type    | Location     | Description                                                                                         |
| ---------------- | ------- | ------------ | --------------------------------------------------------------------------------------------------- |
| `deactivateOnly` | boolean | Query string | When `true`, disable access without destructive deletion. Absent or `false` means full deprovision. |

**Request field reference:**

| Field                     | Type             | Required | Description                                                             |
| ------------------------- | ---------------- | -------- | ----------------------------------------------------------------------- |
| `visitId`                 | UUID v4          | Yes      | Visit identifier from Provision.                                        |
| `siteId`                  | UUID v4          | Yes      | Sine site identifier.                                                   |
| `externalId`              | string           | No       | Integration reference string.                                           |
| `credentialType`          | string           | No       | Credential type.                                                        |
| `accessControlVisitId`    | string           | Yes      | The ACS identifier from the Provision response.                         |
| `accessControlLocationId` | string           | No       | The ACS location identifier from the Provision response, if applicable. |
| `badgeNumber`             | string           | Yes      | The credential value from the Provision response.                       |
| `accessGroups`            | array of strings | No       | Access group IDs associated with this visit.                            |

**Success response** (`200 OK`):

```json
{
  "code": "DEPROVISIONED"
}
```

**Notes:**

- Deprovision is **asynchronous from Sine's perspective**. Sine marks the visitor as checked out regardless of the outcome.
- Your endpoint must be idempotent. If the credential is already revoked or does not exist, return `DEPROVISIONED` rather than an error.
- When `deactivateOnly=true`, preserve the cardholder and credential record in the ACS. See [Physical Card Inventory and Asset Management](<./Getting Started.mdx>) for context.

## Access Groups Endpoint

The Access Groups endpoint is **optional**. When configured, it allows Sine administrators to browse and assign access groups from your ACS directly within Sine's integration settings.

**Method:** `GET`

**Example request:**

```javascript
GET https://your-endpoint.example.com/access-groups?externalId=xyz
```

**Query parameters:**

| Parameter      | Type                | Description                                                                                                 |
| -------------- | ------------------- | ----------------------------------------------------------------------------------------------------------- |
| `externalId`   | string              | Integration reference string. Present if configured.                                                        |
| `nameIncludes` | string              | Case-insensitive substring filter on group name. Optional.                                                  |
| `id`           | string (repeatable) | Fetch by exact group ID. Multiple `id` params may be sent. `id` and `nameIncludes` are never sent together. |
| `after`        | string              | Pagination cursor from a previous `nextLink` response.                                                      |

**Success response** (`200 OK`):

```json
{
  "results": [
    { "name": "Level 1", "id": "782b7f6e-ef97-4242-9c58-81cc0e3af04d" },
    { "name": "Level 2", "id": "5bdd1fa4-8ad7-4fdf-8893-30bd65c28bcf" }
  ],
  "nextLink": "https://your-endpoint.example.com/access-groups?externalId=xyz&after=5bdd1fa4-8ad7-4fdf-8893-30bd65c28bcf"
}
```

**Response field reference:**

| Field            | Type   | Required | Description                                                                                          |
| ---------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------- |
| `results`        | array  | Yes      | List of access groups. Each entry must include `id` and `name`.                                      |
| `results[].id`   | string | Yes      | Opaque ACS identifier for the group. This is the value sent in `accessGroups` on lifecycle requests. |
| `results[].name` | string | Yes      | Human-readable group name shown in the Sine interface.                                               |
| `nextLink`       | string | No       | Full URL for the next page of results. Omit when there are no further pages.                         |

**Notes:**

- `nextLink` must be a directly callable URL - Sine follows it as-is.
- If your ACS does not support filtering, return all groups regardless of query parameters.
- You may set `Cache-Control: max-age=<seconds>` on the response. Sine will honour client-side caching for the specified duration. Cache is invalidated on API key change, external ID change, or Verify/Provision errors.

## Versioning and Compatibility

The ACI integration contract follows an **additive compatibility model**:

- Sine may add new optional fields to request bodies in future updates. Your integration must ignore unknown fields gracefully.
- Sine will not remove or rename existing required fields without a versioned deprecation path.
- Your response payloads should include at minimum the documented required fields. Sine ignores any additional fields you include.

**Practical guidance:**

- Use permissive JSON parsing - ignore unknown keys rather than hard-failing.
- Do not reject requests that contain new or unexpected fields.
- Validate that all required response fields are present before returning a success code from your own handlers.

***

#
