Manual verification
Record that an employee checked the customer's age in person, for example from a physical ID. The employee identifies themselves with their 6-digit access code, and the transaction is recorded against them.
Flow
- The employee enters their access code. Look it up with
GET /api/v1/employees/code?code=to get the employee'sid. - Send
POST /api/v1/verificationswith"method": "manual"and thatemployeeId.
The access code is checked at the lookup step. The verification request itself identifies the employee by employeeId and does not take the code.
Both calls accept a service client (Bearer) or a linked device (DPoP plus a DPoP proof header, see Device authentication). Dashboard user tokens get 403.
Step 1: look up the employee
/api/v1/employees/codeFind an active employee in your tenant by their 6-digit access code. Requires the employees:read scope. Callers: service client (Bearer) or linked device (DPoP). A dashboard user token gets 403.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client) or DPoP <access_token> (linked device). Requires the employees:read scope |
| DPoP | string | Optional | Device callers only: a DPoP proof for this request. The htu is the URL without the query string |
AuthorizationstringRequiredBearer <access_token> (service client) or DPoP <access_token> (linked device). Requires the employees:read scope
DPoPstringOptionalDevice callers only: a DPoP proof for this request. The htu is the URL without the query string
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| code | string | Required | The employee's 6-digit access code. 404 if no active employee in your tenant has it |
codestringRequiredThe employee's 6-digit access code. 404 if no active employee in your tenant has it
Examples
cURL
curl "https://abyrgverslun.is/api/v1/employees/code?code=483920" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response
{
"data": {
"id": "kpwzv8tj1f5vhpvnra1dgwtamapfjw2g",
"name": "Jón Jónsson",
"updatedAt": 1758292800000,
"createdAt": "2025-09-19T14:40:00.000Z"
}
}createdAt is an ISO 8601 string and updatedAt is epoch milliseconds (a number). An unset updatedAt is left out.
Step 2: record the verification
/api/v1/verificationsRecord a manual verification with "method": "manual". The employee decides: "result": "pass" (the default) records that the customer meets requiredAge, "result": "fail" records a refusal. Either way the transaction records the employee as verifiedBy and you get a transactionId. Requires the transactions:write scope.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client) or DPoP <access_token> (linked device). Requires the transactions:write scope |
| DPoP | string | Optional | Device callers only: a DPoP proof for this request (htm POST, htu https://abyrgverslun.is/api/v1/verifications, ath) |
| Content-Type | string | Required | application/json |
| Idempotency-Key | string | Optional | A unique key per verification, such as a UUID (1 to 255 printable ASCII characters). Send the same key when retrying, and you get the original response back instead of a second verification. See Retrying safely on the Pass verification page |
AuthorizationstringRequiredBearer <access_token> (service client) or DPoP <access_token> (linked device). Requires the transactions:write scope
DPoPstringOptionalDevice callers only: a DPoP proof for this request (htm POST, htu https://abyrgverslun.is/api/v1/verifications, ath)
Content-TypestringRequiredapplication/json
Idempotency-KeystringOptionalA unique key per verification, such as a UUID (1 to 255 printable ASCII characters). Send the same key when retrying, and you get the original response back instead of a second verification. See Retrying safely on the Pass verification page
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| method | string | Required | "manual" |
| employeeId | string | Required | The id from the employee lookup. 404 if it isn't an active employee in your tenant (including a malformed or deleted one) |
| requiredAge | integer | Required | Minimum age the employee checked for, 0 to 150 (for example, 20) |
| result | string | Optional | "pass" (default) if the customer meets requiredAge, "fail" to record a refusal. A refusal is stored as a failed verification (failReason underage) with its own transactionId |
| posReference | string | Optional | Your own reference, such as a receipt or basket ID, 1 to 20 characters. Stored on the transaction |
| deviceId | string | Optional | Service clients only: attribute the transaction to an active device of origin api in your tenant. 404 if the device doesn't exist, 400 if it isn't an active api device. Ignored for device callers, which are always recorded as themselves |
| verifiedBy | string | Optional | The Verifier. For a manual verification it must equal employeeId (400 otherwise). Becomes required, and the employee must have completed Employee Enrollment (403 verifier-not-enrolled otherwise), once enrollment is enforced |
methodstringRequired"manual"
employeeIdstringRequiredThe id from the employee lookup. 404 if it isn't an active employee in your tenant (including a malformed or deleted one)
requiredAgeintegerRequiredMinimum age the employee checked for, 0 to 150 (for example, 20)
resultstringOptional"pass" (default) if the customer meets requiredAge, "fail" to record a refusal. A refusal is stored as a failed verification (failReason underage) with its own transactionId
posReferencestringOptionalYour own reference, such as a receipt or basket ID, 1 to 20 characters. Stored on the transaction
deviceIdstringOptionalService clients only: attribute the transaction to an active device of origin api in your tenant. 404 if the device doesn't exist, 400 if it isn't an active api device. Ignored for device callers, which are always recorded as themselves
verifiedBystringOptionalThe Verifier. For a manual verification it must equal employeeId (400 otherwise). Becomes required, and the employee must have completed Employee Enrollment (403 verifier-not-enrolled otherwise), once enrollment is enforced
Examples
cURL (service client)
curl -X POST https://abyrgverslun.is/api/v1/verifications \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"method": "manual",
"employeeId": "kpwzv8tj1f5vhpvnra1dgwtamapfjw2g",
"requiredAge": 20,
"posReference": "till4-1234",
"deviceId": "khh2hxc6ghkzahz07azpb7qhn77m4w09"
}'JavaScript
const headers = { 'Authorization': `Bearer ${accessToken}` };
// Step 1: look up the employee by the code they entered
const lookup = await fetch(
`https://abyrgverslun.is/api/v1/employees/code?code=${encodeURIComponent(code)}`,
{ headers },
);
if (lookup.status === 404) {
throw new Error('Unknown access code');
}
const { data: employee } = await lookup.json();
// Step 2: record the verification
const response = await fetch('https://abyrgverslun.is/api/v1/verifications', {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'manual',
employeeId: employee.id,
requiredAge: 20,
posReference: 'till4-1234',
}),
});
const { result, transactionId } = await response.json();
// result === 'pass' (or 'fail' if you sent result: 'fail')Response
{
"result": "pass",
"transactionId": "k1jcce4akgxtehpqa9pz2809dnxpxhfq",
"livemode": true
}Errors
Both calls return Problem Details (application/problem+json). See Errors. No transaction is recorded when a request is refused.
| Status | When |
|---|---|
| 400 | The body is not valid JSON, deviceId names a device that is not an active api device, verifiedBy is not employeeId (or is missing, once enrollment is enforced), or the Idempotency-Key header is empty, longer than 255 characters or not printable ASCII |
| 401 | Missing, invalid, expired or revoked token, or a bad DPoP proof |
| 403 | The token lacks the scope (employees:read for the lookup, transactions:write for the verification), or it is a dashboard user token. Type verifier-not-enrolled: the employee has not completed Employee Enrollment |
| 404 | No active employee has that access code, the employeeId isn't an active employee in your tenant, or deviceId doesn't match a device in your tenant |
| 422 | Validation failed: a missing code query parameter, or a body with a missing field or a value out of range. Also idempotency-key-reused: the Idempotency-Key was already used with a different body |
To read a manual verification back later, call GET /api/v1/verifications/{transactionId} (see eID verification) or the Transactions API.