This chapter is the endpoint reference for the lookup data that everything else points at: locations, departments, organisations, roles, cost centers, vehicles, visit reasons, access control lists and access control points.

These records rarely change, but they have to exist before anything else can be created. A person, a user or a visitor invitation references them by UUID, and a reference to a UUID that does not exist is rejected. If you are building a first integration, create your locations, departments, organisations and roles first, keep the UUIDs the create calls hand back, and use those when you start creating people and invitations.

Three of these resources are read-only through the API: vehicles, access control lists and the listing of access control points. They are maintained in the admin screens and exposed here so an integration can resolve their UUIDs.

People, credentials and watchlists are documented in People and Credentials API.

Authentication

Every endpoint on this page requires an API key. Send the key in the Authorization request header. The Bearer prefix is optional and is stripped if present, so both of these are accepted:

Authorization: <api-key-token>
Authorization: Bearer <api-key-token>

A token shorter than 64 characters or longer than 256 characters is rejected before anything else happens. Create keys and grant their read and write permissions under Configuration > System Settings > Web Service API; see API Key Configuration.

Each resource below names the permissions it checks. A key that authenticates but lacks the permission gets 403, never a partial result.

Scoping

An API key belongs to exactly one system. Every endpoint on this page resolves the calling system from the key and filters on it, so a key can only ever read, change or delete records belonging to its own system. There is no parameter to widen that scope, and a UUID that exists in another system returns 404 rather than data.

Rate limiting

Requests are metered per API key. When the bucket is empty the response is 429 and the standard Retry-After response header gives the number of seconds to wait before retrying. Buckets are per server node and this deployment is not session-pinned, so treat the value as a lower bound and a hint rather than a cluster-wide guarantee: honour it, then back off further if you are throttled again. Triggering an access control point relay is the most expensive call on this page.

Conventions used on this page

  • Identifiers are always UUIDs. Path parameters, references between records and identifiers in responses are all UUIDs. Internal numeric identifiers are never accepted and never returned.
  • Timestamps are ISO 8601 with an explicit offset, for example 2026-02-02T12:40:57.000+0000. Values are stored and returned in UTC.
  • Successful writes return an acknowledgement, not the changed record:
{"code": 201, "message": "success", "uuid": "8175be3a-461a-4608-90d5-1168b4d06126"}

Re-read the record with its GET endpoint if you need the stored values back.

  • Errors return {"code": <status>, "reason": "<explanation>"}. Field-level validation failures return 422 and list the offending fields in reason.
  • uuid in a request body is ignored on update. The UUID in the URL identifies the record.
  • Every delete on this page is a hard delete unless the section says otherwise. The row is removed, and records that referenced it lose the reference.
  • Common status codes, unless a section says otherwise: 400 malformed or missing body, 401 missing or invalid key, 403 key lacks the permission, 404 unknown UUID, 409 duplicate name, 422 field validation failed, 429 rate limited.

The simple name-only resources

Departments, organisations, groups, cost centers and visit reasons are all the same shape: a UUID and a name, with the same five endpoints and the same behaviour. They differ only in their path, their name-length limit and the permissions they check. Each has its own section below so you can look one up directly, but if you have integrated one you have integrated all of them.

Locations

A location is a physical place: a building, a floor, an estate, an apartment. Locations are the backbone of the product. Visitor self-registration links are per location, required-field policies are per location, and a person or user is normally attached to the location they belong to.

Permissions: Read Locations for the two read endpoints, Write Locations for create, update and delete.

Location types (type): REGION, CAMPUS, BUILDING, FLOOR, AREA, ESTATE, STAND, COMPLEX, APARTMENT.

GET /api/ws/v1/locations

Lists every location. Read-only. No parameters, no pagination.

Returns a short form with only the identifier and the name, which is what you want for populating a picker:

[{"uuid": "b203486c-5ac3-41a6-8eb3-e79d64649128", "name": "Garden Estate 1"}]

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/locations/{uuid}

Reads one location in full. Read-only.

Returns uuid, name, type, complex, address, suburb, postalCode, city, stateProvince, country, effectiveDate, expiresDate, keyContactName, keyContactNumber, latitude, longitude and comments.

Status codes: 200, 400, 401, 403, 404, 429.

POST /api/ws/v1/locations

Creates a location. Returns 201.

Field Type Constraint
name string Required. 2 to 100 characters.
type enum Required. One of the location types above.
complex string 2 to 100 characters.
address string 2 to 100 characters. The street address.
suburb string 2 to 100 characters.
postalCode string 2 to 100 characters.
city string 2 to 100 characters.
stateProvince string 2 to 100 characters.
country string Max 100 characters.
effectiveDate timestamp When the location comes into use.
expiresDate timestamp When it goes out of use.
keyContactName string 2 to 100 characters.
keyContactNumber string 2 to 100 characters, international notation, for example +12125680012.
latitude decimal Decimal degrees, greater than -90.0 and less than 90.0, up to 15 decimal places.
longitude decimal Decimal degrees, greater than -180.0 and less than 180.0, up to 15 decimal places.
comments string Max 5000 characters.

Status codes: 201, 400, 401, 403, 409 (a location with that name exists), 422, 429.

curl -X POST 'https://service.evtrack.com/api/ws/v1/locations' \
  -H 'Authorization: Bearer <api-key-token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "EvTrack Labs - Head Office",
    "type": "BUILDING",
    "address": "64 Burg Street",
    "city": "Cape Town",
    "country": "South Africa",
    "keyContactName": "Alexander Grant",
    "keyContactNumber": "+12125680012"
  }'

PUT /api/ws/v1/locations/{uuid}

Updates a location. Returns 200. Same body and same constraints as create.

Unlike persons and users, this is a partial update: a field you leave out keeps its stored value. Send only the fields you want to change. uuid in the body is ignored.

Status codes: 200, 400, 401, 403, 404, 409, 422, 429.

DELETE /api/ws/v1/locations/{uuid}

Deletes a location. Returns 200. Hard delete.

Status codes: 200, 400, 401, 403, 404, 429.

Departments

A department is an internal organisational unit: Marketing, Facilities, Engineering. Persons and users can be attached to one.

Permissions: Read Departments for the two read endpoints, Write Departments for create, update and delete.

GET /api/ws/v1/departments

Lists every department. Read-only. No parameters, no pagination.

[{"uuid": "7aaee0e2-6884-4fd7-ba63-21d76723dce2", "name": "Marketing"}]

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/departments/{uuid}

Reads one department. Read-only. Returns the same object as a list row.

Status codes: 200, 400, 401, 403, 404, 429.

POST /api/ws/v1/departments

Creates a department. Returns 201.

Field Type Constraint
name string Required. 2 to 100 characters.

Status codes: 201, 400, 401, 403, 409, 422, 429.

curl -X POST 'https://service.evtrack.com/api/ws/v1/departments' \
  -H 'Authorization: Bearer <api-key-token>' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Marketing"}'

PUT /api/ws/v1/departments/{uuid}

Renames a department. Returns 200. Body as for create; uuid in the body is ignored.

Status codes: 200, 400, 401, 403, 404, 409, 422, 429.

DELETE /api/ws/v1/departments/{uuid}

Deletes a department. Returns 200. Hard delete. People who referenced it lose the reference.

Status codes: 200, 400, 401, 403, 404, 429.

Organisations

An organisation is an external company: the contractor firm, the supplier, the business leasing a floor. It is what you use to answer “who does this person work for”.

Permissions: Read Organisations for the two read endpoints, Write Organisations for create, update and delete.

GET /api/ws/v1/organisations

Lists every organisation. Read-only. No parameters, no pagination.

[{"uuid": "f0e973d2-2358-43da-80f1-030534efcbb0", "name": "EvTrack Labs"}]

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/organisations/{uuid}

Reads one organisation. Read-only. Returns the same object as a list row.

Status codes: 200, 400, 401, 403, 404, 429.

POST /api/ws/v1/organisations

Creates an organisation. Returns 201.

Field Type Constraint
name string Required. 2 to 100 characters.

Status codes: 201, 400, 401, 403, 409, 422, 429.

curl -X POST 'https://service.evtrack.com/api/ws/v1/organisations' \
  -H 'Authorization: Bearer <api-key-token>' \
  -H 'Content-Type: application/json' \
  -d '{"name": "EvTrack Labs"}'

PUT /api/ws/v1/organisations/{uuid}

Renames an organisation. Returns 200. Body as for create.

Status codes: 200, 400, 401, 403, 404, 409, 422, 429.

DELETE /api/ws/v1/organisations/{uuid}

Deletes an organisation. Returns 200. Hard delete.

Status codes: 200, 400, 401, 403, 404, 429.

Roles

A role is the permission set attached to a login account. Every user has exactly one. A new system ships with an administrator role and at least one ordinary role, and one role is marked as the default that new accounts receive when no role is specified.

Permissions: Read Roles for the two read endpoints, Write Roles for create, update and delete.

Creating a role through the API creates the role record and its name. The individual permissions inside a role are managed in the admin screens, not through this API, so a role created here starts with nothing granted. Create roles in the admin screens if the accounts using them need to be able to do anything.

GET /api/ws/v1/roles

Lists every role. Read-only. No parameters, no pagination.

[{"uuid": "e36c9948-80a9-4a94-bf10-0f709a015b63", "name": "Hosts", "default": true}]

default marks the role assigned to new accounts that do not name one.

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/roles/{uuid}

Reads one role. Read-only. Returns the same object as a list row.

Status codes: 200, 400, 401, 403, 404, 429.

POST /api/ws/v1/roles

Creates a role. Returns 201.

Field Type Constraint
name string Required. 2 to 50 characters.
default boolean Whether new accounts get this role when none is specified.

Status codes: 201, 400, 401, 403, 409, 422, 429.

curl -X POST 'https://service.evtrack.com/api/ws/v1/roles' \
  -H 'Authorization: Bearer <api-key-token>' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Hosts"}'

PUT /api/ws/v1/roles/{uuid}

Updates a role name or its default flag. Returns 200. Body as for create.

Status codes: 200, 400, 401, 403, 404, 409, 422, 429.

DELETE /api/ws/v1/roles/{uuid}

Deletes a role. Hard delete, but only when the role is safe to remove.

Three conditions block deletion, and this endpoint reports them in an unusual way: the HTTP status remains 200 and the failure is carried in the response body, in the code field, with message set to failed. Check the body, not just the status.

Body code Meaning
200 Deleted. message is success.
412 The role still has accounts assigned. Move them to another role first.
417 The role is the administrator role and can never be deleted.
424 The role is the default role for new accounts. Mark another role as default first.

A role UUID that does not exist still returns HTTP 404 in the normal way.

Status codes: 200 (see the body), 400, 401, 403, 404, 429.

Cost Centers

A cost center is the billing or budget code a person is charged against. Persons reference one; users do not.

Permissions: Read Cost Centers for the two read endpoints, Write Cost Centers for create, update and delete.

The equivalent admin screen is Cost Centers.

GET /api/ws/v1/cost-centers

Lists every cost center. Read-only. No parameters, no pagination.

[{"uuid": "8175be3a-461a-4608-90d5-1168b4d06126", "name": "Finance"}]

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/cost-centers/{uuid}

Reads one cost center. Read-only. Returns the same object as a list row.

Status codes: 200, 400, 401, 403, 404, 429.

POST /api/ws/v1/cost-centers

Creates a cost center. Returns 201.

Field Type Constraint
name string Required. 2 to 50 characters.

Status codes: 201, 400, 401, 403, 409, 422, 429.

curl -X POST 'https://service.evtrack.com/api/ws/v1/cost-centers' \
  -H 'Authorization: Bearer <api-key-token>' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Finance"}'

PUT /api/ws/v1/cost-centers/{uuid}

Renames a cost center. Returns 200. Body as for create.

Status codes: 200, 400, 401, 403, 404, 409, 422, 429.

DELETE /api/ws/v1/cost-centers/{uuid}

Deletes a cost center. Returns 200. Hard delete.

Status codes: 200, 400, 401, 403, 404, 429.

Visit Reasons

A visit reason is the drop-down entry a receptionist or a self-registering visitor picks to say why they are here: Delivery, Contractor, Interview. Reasons are referenced when creating invitations and registrations.

Permissions: Read Visit Reasons for the two read endpoints, Write Visit Reasons for create, update and delete.

Note the path is singular: /api/ws/v1/visit-reason.

The equivalent admin screen is Visit Reasons.

GET /api/ws/v1/visit-reason

Lists every visit reason. Read-only. No parameters, no pagination.

[{"uuid": "8175be3a-461a-4608-90d5-1168b4d06126", "name": "Delivery"}]

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/visit-reason/{uuid}

Reads one visit reason. Read-only. Returns the same object as a list row.

Status codes: 200, 400, 401, 403, 404, 429.

POST /api/ws/v1/visit-reason

Creates a visit reason. Returns 201.

Field Type Constraint
name string Required. 2 to 100 characters.

Status codes: 201, 400, 401, 403, 409, 422, 429.

curl -X POST 'https://service.evtrack.com/api/ws/v1/visit-reason' \
  -H 'Authorization: Bearer <api-key-token>' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Delivery"}'

PUT /api/ws/v1/visit-reason/{uuid}

Renames a visit reason. Returns 200. Body as for create.

Status codes: 200, 400, 401, 403, 404, 409, 422, 429.

DELETE /api/ws/v1/visit-reason/{uuid}

Deletes a visit reason. Returns 200. Hard delete. Existing visits keep the text they were recorded with; the reason simply stops being offered.

Status codes: 200, 400, 401, 403, 404, 429.

Vehicles

A vehicle is a car on record, either belonging to a person or captured during a visit. Vehicles are created as a side effect of registering people and visits, and by plate-reading cameras, so the API exposes them read-only: there is no create, update or delete.

Permission: Read Vehicles.

GET /api/ws/v1/vehicles

Lists vehicles, paged. Read-only.

Parameter Type Notes
start integer Zero-based index of the first row. Defaults to 0.
limit integer Rows to return. Between 100 and 1000. Defaults to 500.

Response:

{
  "entries": [
    {
      "uuid": "7aaee0e2-6884-4fd7-ba63-21d76723dce2",
      "licensePlate": "JN1BY1WP",
      "vin": "JN1BY1AR3BM374797",
      "make": "VW",
      "model": "Golf MK8",
      "color": "Blue",
      "plateCountry": "ARE",
      "plateRegion": "AUH",
      "plateCategory": "B"
    }
  ],
  "startIndex": 0,
  "countReturned": 1,
  "totalCount": 1204
}

Plate numbers and vehicle identification numbers are stored with punctuation and spacing stripped, so AB 12 CD is stored and returned as AB12CD. Compare on the stripped form when you reconcile against your own system.

Status codes: 200, 400, 401, 403, 429.

curl -X GET 'https://service.evtrack.com/api/ws/v1/vehicles?start=0&limit=100' \
  -H 'Authorization: Bearer <api-key-token>'

Access Control Lists

An access control list is a named bundle of access rules, where each rule pairs an access control point with a schedule. Assigning a list to a credential is what actually grants access: a credential with no list identifies its holder and opens nothing.

The API exposes access control lists read-only, so an integration can resolve the UUIDs it needs when creating or updating credentials. Lists themselves, and the rules inside them, are built in the admin screens; see Access Control Lists.

Permission: Read Access Control Lists.

GET /api/ws/v1/access-control-lists

Lists every access control list. Read-only. No parameters, no pagination.

[{"uuid": "7aaee0e2-6884-4fd7-ba63-21d76723dce2", "name": "Main Building Access"}]

Feed these UUIDs into the accessControlListUuids field when creating or updating a credential; see People and Credentials API.

Status codes: 200, 400, 401, 403, 429.

curl -X GET 'https://service.evtrack.com/api/ws/v1/access-control-lists' \
  -H 'Authorization: Bearer <api-key-token>'

Access Control Points

An access control point is a physical thing that opens: a door, a turnstile, a vehicle gate, an elevator, a portal or a manned checkpoint. Points are bound to reader hardware and grouped into access control lists.

Points are created and wired to hardware in the admin screens; see Access Control Points. The API offers a read-only listing plus one action: triggering the point’s relay, which is how an external system opens a door on its own authority.

Permissions: Read Access Control Points for the listing, Access Control Point Relay Control for the trigger. These are separate permissions on purpose, so a key that reads your access topology cannot open anything.

Point types (type): DOOR, ELEVATOR, PORTAL, TURNSTILE, VEHICLE_GATE, CHECK_POINT.

GET /api/ws/v1/access-control-points

Lists every access control point. Read-only. No parameters, no pagination.

[{"uuid": "aa22074f-3361-45a9-9517-9d59789d2832", "name": "Reception Turnstile", "type": "TURNSTILE"}]

Status codes: 200, 400, 401, 403, 429.

GET /api/ws/v1/access-control-points/trigger/{uuid}

Fires the access-granted relay on the named point, opening it once. Returns 200.

This is the one action on this page that changes something in the physical world. It records an audit event naming the API key that fired it, and it is metered far more heavily than any read, so it cannot be used to hold a door open by repetition.

The trigger is unconditional. It does not check a credential, a schedule or a validity window, so treat the key that carries this permission as you would a physical master key: give it to one integration, and to no other.

Status codes: 200, 400, 401, 403, 404, 429.

curl -X GET 'https://service.evtrack.com/api/ws/v1/access-control-points/trigger/aa22074f-3361-45a9-9517-9d59789d2832' \
  -H 'Authorization: Bearer <api-key-token>'

Back to top

Copyright EvTrack. All rights reserved.

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