The Web Service API is EvTrack’s REST interface for other systems. A CRM, booking engine, hotel or hospital management system, building portal, or in-house application can use it to pre-register visitors, issue and revoke temporary visitor credentials, drive a visit through its status lifecycle, read and maintain personnel and users, look up reference data such as locations and visit reasons, upload photos, and feed card or QR code reads into access control from software instead of physical hardware.

Everything the API can do is also possible in the admin interface. The API exists so an upstream system can do it automatically, at the moment its own workflow demands it - for example creating a visitor invitation the instant a meeting is booked, and cancelling it the instant the meeting is called off.

Before you start you need three things:

  1. An administrator account that may manage API keys.
  2. An API key with the right permissions ticked. See API Key Configuration.
  3. The base URL of your EvTrack server. On the hosted service this is https://service.evtrack.com. On premises it is whatever address your server is published on.

1. Base URL and versioning

Every endpoint lives under a single, versioned prefix:

https://<your-evtrack-server>/api/ws/v1/<resource>
  • v1 is the current interface version. It is part of the path, so a future version can be introduced beside it without breaking existing integrations.
  • All traffic must use HTTPS. Keys travel in a request header, so plain HTTP would expose them.
  • The machine-generated reference for every endpoint, field, and schema is published at https://api.docs.evtrack.com/. Use that for the exact field-by-field contract; use these pages for what the endpoints mean and when to call them.

2. Authentication

Send the API key in the standard Authorization request header on every call:

Authorization: LeM7xfYqRM2ylpOhQIYmOnDNKJCGChkfiPb7HE/z4+igETi+kunzwNOj1NY9+705fT589c1oh04HHSohamdE40zuZpv/3EweWMnVzSXo89c=
  • The header value is the key exactly as EvTrack displayed it when the key was created. A Bearer prefix is optional: it is accepted and stripped, so Authorization: Bearer <key> works identically. There is no separate login call, no session, and no cookie: each request stands alone.
  • The key length is checked first, before the key is even parsed: a value shorter than 64 or longer than 256 characters is rejected as unauthorized immediately.
  • There is no username or password anywhere in the API. To cut an integration off, disable or delete its key.
  • Every request is scoped to the system the key belongs to. A key can only read and change records in its own system, so a UUID that exists elsewhere reads as 404.

Your API keys

Creating a key. Open Configuration > System Settings > Web Service API, click Add, name the key after the application that will use it, tick only the permissions that application needs, tick Enable, and save. The key value is displayed once, immediately after saving, and can never be shown again - copy it into the consuming application straight away. Full walkthrough, including the optional payload encryption settings: API Key Configuration.

Permissions. Rights are granted per key, per resource, and separately for reading and writing. If a key calls an endpoint whose right is not ticked, the call fails with 403 and nothing is read or changed. Grant the narrowest set that the integration actually needs, and use a separate key per application so one can be revoked without disturbing the others.

What the key may do

Optional payload encryption. A key can also carry an Encryption Secret and Encryption IV. When both are set, EvTrack decrypts the sensitive fields of every request payload and encrypts them again in every response, using AES-256 in CBC mode. The field list, the override option, and worked encrypt and decrypt code are documented in API Key Configuration.


3. Request and response conventions

Content type. Request bodies are JSON and must be sent with Content-Type: application/json. Responses are JSON, except where an endpoint returns an image or a file. The single exception on the request side is the photo upload endpoint, which takes multipart/form-data.

Identifiers. Records are addressed by UUID in the path, never by an internal number:

GET /api/ws/v1/invites/7aaee0e2-6884-4fd7-ba63-21d76723dce2

A visitor invitation’s UUID is called the registration ID, and it is returned as registrationId when the invitation is created. A value that is not a valid UUID is rejected with 422 before the endpoint runs.

Field naming. Response fields use camel case (firstName, registrationId). Visitor invitation payloads additionally accept the underscore spellings first_name, last_name, identity_number, alternative_number and visit_type on input, so older integrations keep working.

Unknown fields are ignored. A field the interface does not recognise is discarded rather than rejected, so adding your own bookkeeping fields to a payload will not break a call. Do not rely on them coming back.

Date and time. All date/time fields are ISO-8601 timestamps that carry their own offset, for example 2026-07-07T13:25:43.366Z. The trailing Z means UTC, and UTC is the recommended way to send them: an absolute instant cannot be misread. A different offset such as 2026-07-07T15:25:43.366+02:00 is accepted and converted. A timestamp with no offset is ambiguous and should be avoided. Responses always render date/time values in UTC.

The values you send are absolute moments in time, not wall-clock times in the operator’s region. The admin interface displays them in the configured display time zone, so an invitation sent as 07:00Z shows as 09:00 to an operator in a region two hours ahead of UTC. Send UTC, and let the interface do the conversion.

Validity windows are rounded. When an invitation is created or updated, the activation time is rounded down to the start of its minute and the expiry time is rounded up to the last second of its minute. A window of 09:00:30 to 17:00:10 is stored as 09:00:00 to 17:00:59, so a credential is never a few seconds short of the minute an operator sees on screen.

Pagination. List endpoints take start (zero-based index of the first row, default 0) and limit (rows per page, 1 to 1000, default 100) as query parameters, and answer with the rows plus the counters needed to page through them:

{
  "entries": [ ... ],
  "startIndex": 0,
  "countReturned": 100,
  "totalCount": 2841
}

Page by increasing start by limit until startIndex + countReturned reaches totalCount. A limit above 1000 is rejected.

Filtering. Filters are endpoint-specific query parameters, for example filter=CURRENT and visitorUUID=<uuid> on the invitation list. They are documented with each endpoint in the chapters listed below.


4. Errors and status codes

An error response carries a numeric code and a human-readable reason:

{
  "code": 422,
  "reason": "422 - Unprocessable Entity - Invalid Fields: [@email: must be a valid email address]"
}

Field-level validation failures add an errors object keyed by field name. Rejected values are never echoed back, so an error response is safe to log.

Code Meaning Typical cause
200 OK Read, update, delete, or status change succeeded
201 Created A new visitor invitation was created
400 Bad Request Missing or unreadable JSON body, missing path parameter, limit out of range, unsupported image type on upload
401 Unauthorized Missing, malformed, disabled, or unknown API key. On the virtual reader it also means the credential was DENIED
403 Forbidden The key is valid but that permission is not ticked
404 Not Found No record with that UUID, or it belongs to a different environment
409 Conflict The transaction has already been recorded
412 Precondition Failed A signed media link has passed its expiry
415 Unsupported Media Type The credential needs a second factor that this interface cannot supply
422 Unprocessable Entity Field validation failed, a date/time could not be parsed, a UUID path value is malformed, or a referenced location, host, or visit reason does not exist
424 Failed Dependency No access control point is wired to the virtual reader interface
425 Too Early The update is identical to what is already stored, so nothing was changed
429 Too Many Requests The key’s rate limit is exhausted
451 Unavailable For Legal Reasons The visitor matched a watchlist entry and was denied

Treat 422 and 451 as final: retrying the identical request produces the identical result. Treat 429 as temporary.

Rate limiting. Each API key has its own allowance of 2000 units, replenished at 800 units per minute. A create or update of an invitation, a photo upload, and a photo download each cost 50 units; list, read, delete, and status-change calls cost 30; a status read or file delete costs 10. A failed authentication attempt costs 400 units, so a client looping on a bad key throttles itself quickly. When the allowance runs out the API answers 429; back off and retry, doubling the wait each time rather than retrying immediately.


5. Restricting the API to the local machine

On-premises installations that only ever call the API from software running on the EvTrack server itself can close the interface to the network entirely:

evtrack.security.config.hardening.webservice-api.restrict-to-localhost=true

With this enabled, requests to the API are accepted only from 127.0.0.1 and the IPv6 loopback address; everything else is refused before authentication is even attempted. The default is false. Do not enable it if any integration calls EvTrack from another host. See Security Configuration.


6. Resource index

Resource group Base path Documented in
Visitor invitations /api/ws/v1/invites Invites and Visits
Visit status transitions /api/ws/v1/invites/{registrationId}/status Invites and Visits
Users /api/ws/v1/users People and Credentials API
Personnel /api/ws/v1/persons People and Credentials API
Credentials /api/ws/v1/credentials People and Credentials API
Locations /api/ws/v1/locations Reference Data API
Departments /api/ws/v1/departments Reference Data API
Organisations /api/ws/v1/organisations Reference Data API
Groups /api/ws/v1/groups Reference Data API
Roles /api/ws/v1/roles Reference Data API
Cost centers /api/ws/v1/cost-centers Reference Data API
Visit reasons /api/ws/v1/visit-reason Reference Data API
Vehicles /api/ws/v1/vehicles Reference Data API
Watchlists /api/ws/v1/watchlists Reference Data API
Access control points /api/ws/v1/access-control-points Reference Data API
Access control lists /api/ws/v1/access-control-lists Reference Data API
Photo upload and delete /api/ws/v1/files Files and Media
Photo and thumbnail download /api/ws/v1/photos Files and Media
Signed media links /api/ws/v1/storage Files and Media
Virtual reader /api/ws/v1/virtual/reader Files and Media
Outbound event delivery (your endpoints) Webhooks

Two configuration screens change how the API behaves and are worth reading before you build against it:

  • Web Service API (Visitors) - which visitor fields are mandatory on an incoming registration, and whether an unrecognised location name is tolerated or rejected.
  • Web Service API - the API key list itself.

7. Worked example: create a visitor invitation

curl -X 'POST' \
  'https://service.evtrack.com/api/ws/v1/invites' \
  -H 'accept: application/json' \
  -H 'Authorization: LeM7xfYqRM2ylpOhQIYmOnDNKJCGChkfiPb7HE/z4+igETi+kunzwNOj1NY9+705fT589c1oh04HHSohamdE40zuZpv/3EweWMnVzSXo89c=' \
  -H 'Content-Type: application/json' \
  -d '{
  "activation": "2026-07-07T13:25:43.366Z",
  "email": "alexander.grant@evtrack.com",
  "expiry": "2026-07-10T13:25:43.366Z",
  "first_name": "Alexander",
  "last_name": "Grant",
  "location": "542 Main Street",
  "mobile": "+12125680012"
}'

Response:

{
  "code": 201,
  "message": "created",
  "registrationId": "57fa54a4-d6a5-4a04-a559-b97d6043dccf",
  "visitorId": "b793da5f-476c-4f54-9eb5-daa4db21d70d",
  "type": "QR_CODE",
  "accessCode": "d617278d",
  "inviteLink": "https://app.evtrack.com/i/u/57fa54a4-d6a5-4a04-a559-b97d6043dccf"
}

Keep the registrationId. Every later call about this visit - read it, update it, cancel it, check the visitor in or out - is addressed by that value.


8. Worked example: find a user by mobile number

User lookups use the search endpoint, which takes its criteria in the request body. The plain GET /api/ws/v1/users listing is deprecated for searching; use POST /api/ws/v1/users/search.

The criteria field for a phone number is mobileNumber, not mobile. This matters: an unrecognised criteria field is silently ignored rather than rejected, so "mobile": "+12125680012" returns the unfiltered first page instead of the one user you were looking for. The other criteria are firstName, lastName, email and identityNr, alongside the start and limit paging values.

curl -X 'POST' \
  'https://service.evtrack.com/api/ws/v1/users/search' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer LeM7xfYqRM2ylpOhQIYmOnDNKJCGChkfiPb7HE/z4+igETi+kunzwNOj1NY9+705fT589c1oh04HHSohamdE40zuZpv/3EweWMnVzSXo89c=' \
  -H 'Content-Type: application/json' \
  -d '{
  "start": 0,
  "limit": 500,
  "mobileNumber": "+12125680012"
}'

Response:

{
  "entries": [
    {
      "uuid": "21bf9d3b-8c8f-4eec-a398-c9650d0dc2c0",
      "email": "alexander.grant@evtrack.com",
      "firstName": "Alexander",
      "lastName": "Grant",
      "mobile": "+12125680012"
    }
  ],
  "startIndex": 0,
  "countReturned": 1,
  "totalCount": 50
}

The returned uuid is what you pass as hostUser when creating an invitation, so the visit is attributed to the right host.


Table of contents


Back to top

Copyright EvTrack. All rights reserved.

Page last modified: 2026-09-28 15:32.