> ## Documentation Index
> Fetch the complete documentation index at: https://developers.vizito.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction

> Base URL, conventions and everything that is true of every endpoint.

The Vizito API is a JSON API over HTTPS. Every endpoint lives under one base URL, takes and returns JSON, and is authenticated with an API key.

<Card title="Base URL" icon="link">
  ```text theme={null}
  https://api.vizito.eu/api
  ```
</Card>

Every path in this reference is relative to that base URL. `GET /companiesList` means `GET https://api.vizito.eu/api/companiesList`.

## Authentication

Send your API key as a bearer token on every request:

```bash theme={null}
curl https://api.vizito.eu/api/companiesList \
  -H "Authorization: Bearer vzk_1a2b3c4d5e6f7890_..."
```

Keys are created by a global admin on the [Integrations page](https://backoffice.vizito.be/#/app/integrations) of the Backoffice. See [Authentication](/api-reference/authentication) for expiry, IP restrictions, Microsoft Entra ID and what a key is not allowed to do.

## Everything hangs off a location

Almost every endpoint names a location, either in the path (`/companies/{company_id}/...`) or in the body (`company_id`). Your key is pinned to a set of locations, and naming one outside that set is refused with `403`.

Start with [`GET /companiesList`](/api-reference/locations/listing-locations): it is the only endpoint that needs no location id, and it returns the ones your key covers.

## Conventions

<AccordionGroup>
  <Accordion title="Ids" icon="fingerprint">
    Every id is a 24-character hexadecimal string, e.g. `5f2a1b9c4d3e2f0011223344`. Objects carry theirs as `_id`. Ids are stable: store them rather than re-resolving objects by name.
  </Accordion>

  <Accordion title="Dates and times" icon="clock">
    Timestamps are ISO 8601 in UTC — `2026-08-29T07:58:11.000Z` — both in and out. Where an endpoint takes a date in the path it also accepts a plain `YYYY-MM-DD`, which is read in the location's own timezone.
  </Accordion>

  <Accordion title="Request bodies" icon="code">
    Send `Content-Type: application/json`. Form-encoded bodies are accepted too, for the sake of older integrations, but JSON is what you want.
  </Accordion>

  <Accordion title="Custom fields" icon="list-check">
    A visit type can define its own questions. Their answers are stored on the visitor or registered visitor under the field's `field_name`, alongside the built-in fields — so you send and read them as ordinary top-level keys. `GET /companies/{company_id}/fields` lists what a location has.
  </Accordion>

  <Accordion title="Extra fields are kept" icon="box-archive">
    Visitors, registered visitors and hosts accept keys beyond the documented ones and store them as-is. Handy for carrying your own reference (`"crm_id": "..."`), but be deliberate: a typo in a field name creates a new field rather than failing.
  </Accordion>

  <Accordion title="Language" icon="language">
    Endpoints that return configured text — visit types, agreements, fields — take a `language_id` query parameter (`en`, `nl`, `fr`, `de`, …) and answer in that language, falling back to English.
  </Accordion>
</AccordionGroup>

## Listing endpoints

The two big lists — visitors and registered visitors — are the ones the Backoffice grids are built on, so they take the grid's parameters:

| Parameter               | Example                                                           | Notes                                             |
| ----------------------- | ----------------------------------------------------------------- | ------------------------------------------------- |
| `count`                 | `count=50`                                                        | Rows to return. **Required.**                     |
| `page`                  | `page=1`                                                          | 1-based. **Required.**                            |
| `sorting[field]`        | `sorting[signed_in]=desc`                                         | `asc` or `desc`. Required on registered visitors. |
| `filter[field]`         | `filter[company]=Acme`                                            | Case-insensitive substring match                  |
| `filter[field]` (dates) | `filter[signed_in][startDate]=...&filter[signed_in][endDate]=...` | Half-open range                                   |

They answer with the page and the unfiltered-by-page total:

```json theme={null}
{ "total": 128, "visitors": [ ... ] }
```

Every other list endpoint returns a plain array.

## Rate limits

There is no general request quota. Two things are limited:

* **Failed key presentations** — 30 refused credentials from one IP address in 15 minutes gets you `429` for the rest of the window. Valid requests never count towards it.
* **Bulk invitations** — capped per location, so a loop cannot mail your whole address book. Over the cap, the extra recipients are reported as `skippedRateLimited` rather than sent.

Still, be reasonable: prefer one call with a page of 100 over a hundred calls, and use [webhooks](/documentation/webhooks) instead of polling every few seconds.

## Errors

Failures come back as an HTTP status with a short JSON body. See [Errors](/api-reference/errors) for what each status means and how to react to it.


## Related topics

- [Introduction](/index.md)
