> For the complete documentation index, see [llms.txt](https://api-docs.visadoc.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://api-docs.visadoc.co/getting-started.md).

# Getting Started

The VisaDoc API lets you do everything the platform does, programmatically: run a visa check (assessment), open and track a visa application, manage travellers and their documents, and read the audit trail.

## Base URL

```bash
https://api.visadoc.co/api:v3
```

All paths below are relative to this. Paths have **no trailing slash**: call `/applications/search`, not `/applications/search/`.

## Authentication

Every endpoint except `POST /token` needs **two headers**: a Bearer token and `X-Data-Source`. Users with the **Admin** role generate tokens themselves with `POST /token`; VisaDoc gives you your `X-Data-Source` value when your instance is set up. See [Authentication](/authentication.md).

## Your first request

Confirm your token and data source work by asking the API who you are:

```bash
curl https://api.visadoc.co/api:v3/users/me \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Data-Source: YOUR_DATA_SOURCE"
```

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "created_at": 1755182769726,
  "name": "John Doe",
  "first_name": "John",
  "last_name": "Doe",
  "email": "john.doe@acme.com",
  "role": "Admin",
  "avatar_url": null,
  "company_name": "Acme Corp"
}
```

A `401` here almost always means one of the two headers is missing or wrong. See [Authentication](/authentication.md).

## Request & response format

* **Two headers on every request:** `Authorization: Bearer …` and `X-Data-Source: …`.
* **JSON in, JSON out.** Send `Content-Type: application/json` on JSON `POST`s. Document upload is `multipart/form-data`.
* **Dates** in requests (trip dates) use `YYYY-MM-DD`.
* **Countries** are always **ISO3** (`USA`, `IND`, `JPN`). ISO2 codes such as `US` won't match.
* **Stored timestamps** (`created_at`) are returned as **epoch milliseconds** (e.g. `1755182769726`), not ISO strings. Filter on them as numbers.
* **IDs:** pass every ID back exactly as the API returned it. Don't parse, convert or build IDs yourself. The API Reference shows the declared type of each field.
* **Be tolerant of new fields.** Responses can gain fields over time. Ignore fields you don't use, and treat any field you do use as possibly `null` or absent.

## Search, lists and paging

Every "list" is a `POST /{resource}/search` with a `{ filters, page, per_page }` body (see [Filters](/filters.md)). Search endpoints only accept `POST`. The response shape depends on the endpoint:

| Endpoint                    | Response                                              |
| --------------------------- | ----------------------------------------------------- |
| `POST /applications/search` | Paged envelope with totals: `itemsTotal`, `pageTotal` |
| `POST /documents/search`    | Paged envelope without totals: `perPage` instead      |
| `POST /applicants/search`   | Plain array                                           |
| `POST /actions/search`      | Plain array                                           |

The paged envelope looks like this:

```json
{
  "itemsReceived": 25,
  "curPage": 1,
  "nextPage": 2,
  "prevPage": null,
  "offset": 0,
  "itemsTotal": 118,
  "pageTotal": 5,
  "items": [ … ]
}
```

* Pages start at `1`.
* On the enveloped endpoints, follow `nextPage` until it is `null`.
* On the plain-array endpoints, request the next page until you get fewer items than `per_page`.
* Always set `per_page` explicitly. Only `applications/search` has a sensible default (`25`).

## The endpoint surface

| Resource    | Method + path                          | What it's for                                                                                          |
| ----------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Token       | `POST /token`                          | Generate an API token (Admin users only)                                                               |
| Assessment  | `POST /assessments/new`                | Run a visa check for a trip; returns entry options                                                     |
|             | `GET /assessments/{assessment_id}`     | Re-open a past check without running it again (read-only)                                              |
| Application | `POST /applications/new`               | Open a case from a chosen entry option                                                                 |
|             | `POST /applications/search`            | Dashboard / work queue (filter by `status`, `payment_status`, `assessment`, …)                         |
|             | `GET /applications/{application_id}`   | Canonical single-case state                                                                            |
| Applicants  | `POST /applicants/new`                 | Create a traveller or manager                                                                          |
|             | `POST /applicants/search`              | Find / list travellers                                                                                 |
|             | `GET /applicants/{applicant_id}`       | One traveller's profile                                                                                |
|             | `DELETE /applicants/{applicant_id}`    | Erase a traveller's personal data (GDPR); see [Trip changes and erasure](/trip-changes-and-erasure.md) |
| Documents   | `POST /documents/search`               | Document work queue across cases                                                                       |
|             | `GET /documents/{document_id}`         | One document's review state                                                                            |
|             | `POST /documents/{document_id}/upload` | Upload the file for a slot (`multipart/form-data`)                                                     |
| Audit       | `POST /actions/search`                 | Status-change / event audit trail                                                                      |
| Access      | `GET /magic_link`                      | Get a passwordless sign-in token for a traveller; see [Magic Link](/magic-link.md)                     |
| Identity    | `GET /users/me`                        | The authenticated caller                                                                               |

## Errors

Standard HTTP status codes:

| Code  | Meaning                                                                 |
| ----- | ----------------------------------------------------------------------- |
| `400` | Bad input: check the request payload                                    |
| `401` | Missing, invalid or expired token, or a missing / wrong `X-Data-Source` |
| `403` | Authenticated but not allowed for this resource                         |
| `404` | Not found                                                               |
| `429` | Rate-limited: back off and retry with an increasing delay               |
| `500` | Server error                                                            |

Error responses carry a JSON body with a machine-readable `code` and a human-readable `message`:

```json
{
  "code": "ERROR_CODE_UNAUTHORIZED",
  "message": "This token belongs to a different datasource."
}
```

Branch on the HTTP status, and log `code` and `message`. When you report a problem to VisaDoc, include the full request (method, path, body, without the token) and the time it was sent. That lets us trace it quickly.

## Live and demo mode

Your instance runs in **live mode**: every assessment and application created there is a real case that VisaDoc processes.

For testing and demos, use **demo mode**:

* Users with the **Admin** role can switch between live and demo mode.
* Users with the **Sales** role work in demo mode only.
* Assessments and applications created in demo mode aren't charged, are marked as demo, and aren't picked up by VisaDoc's operations team.

Ask [**developers@visadoc.co**](mailto:developers@visadoc.co) to set up demo access for your team, and confirm with us which mode your test requests run in before you create assessments or applications.
