---
title: API Reference
subtitle: Connect your website or external tools to Check-in ARTISAN
icon: code-bracket
order: 12
alt_slug: api
excerpt: Use the Check-in ARTISAN REST API to display your company profile, synchronize customers, jobs, site visits, and invoices with your own tools.
---

## Overview

The Check-in ARTISAN API lets you integrate your field service company with your own website or external applications. Whether you want to display your company profile, synchronize customer and job data, or export invoices to your accounting software, the API gives you full programmatic access to your company data.

**Key use cases:**

- Display your company information on your own website.
- Build a custom customer-facing portal.
- Export site visit and invoice data to your accounting software.
- Automate workflows with other business tools.

---

## Base URL

All API requests are made to:

```
https://checkinartisan.app/api/v1/
```

All responses are JSON. All dates and timestamps use the ISO 8601 format.

---

## Authentication

Most API endpoints require authentication via a **Bearer token**. You generate tokens from the admin panel under **Check-in ARTISAN Account > API Tokens**.

Include the token in the `Authorization` header of every authenticated request:

```
Authorization: Bearer your-api-token-here
```

Each token is scoped to a single company. Multiple tokens can be created for the same company (for example, one per integration).

**Security best practices:**

- Treat API tokens like passwords. Never commit them to source code.
- Use one token per integration so you can revoke individual tokens without affecting others.
- Revoke tokens immediately when they are no longer needed.

---

## Public Endpoints

These endpoints require no authentication. They are designed for embedding on public-facing websites.

### GET /api/v1/companies/{slug}

Returns the public profile of a company.

**Parameters:** `{slug}` is the company's unique identifier (visible in your admin URL).

**Response:**

```json
{
  "data": {
    "slug": "swift-plumbing",
    "name": "Swift Plumbing & Co.",
    "description": "Professional plumbing services for residential and commercial clients.",
    "phone": "+33 1 23 45 67 89",
    "mobile": null,
    "email": "contact@swiftplumbing.fr",
    "website": "https://swiftplumbing.fr",
    "address": "12 Rue des Artisans",
    "address2": null,
    "zip_code": "75001",
    "city": "Paris",
    "country": "FR",
    "opening_hours": { "monday": "8:00-18:00" },
    "social": {
      "facebook": null,
      "instagram": "https://instagram.com/swiftplumbing",
      "twitter": null,
      "youtube": null
    },
    "locale": "fr",
    "currency": "EUR",
    "timezone": "Europe/Paris"
  }
}
```

---

## Authenticated Endpoints

All endpoints below require a valid Bearer token in the `Authorization` header.

---

### GET /api/v1/company

Returns the full profile of the company associated with the API token, including the current billing plan.

**Response:** same as the public company endpoint, plus a `"plan"` field (`"free"`, `"solo"`, or `"pro"`).

---

### Customers

#### GET /api/v1/customers

List all customers for your company.

**Query parameters:**

| Parameter  | Type    | Description |
|------------|---------|-------------|
| `search`   | string  | Filter by name, email, or phone. |
| `per_page` | integer | Results per page (default: 25, max: 100). |
| `page`     | integer | Page number (default: 1). |

**Response:**

```json
{
  "data": [
    {
      "id": 1,
      "first_name": "Marie",
      "last_name": "Dupont",
      "full_name": "Marie Dupont",
      "email": "marie@example.com",
      "phone": "+33 6 12 34 56 78",
      "mobile": null,
      "address": "12 Rue des Artisans",
      "address2": null,
      "zip_code": "75001",
      "city": "Paris",
      "country": "FR",
      "notes": null,
      "send_sms": true,
      "is_walk_in": false,
      "created_at": "2026-01-15T10:30:00+01:00",
      "updated_at": "2026-01-15T10:30:00+01:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 3,
    "per_page": 25,
    "total": 72
  }
}
```

#### POST /api/v1/customers

Create a new customer.

**Request body:**

```json
{
  "first_name": "Marie",
  "last_name": "Dupont",
  "email": "marie@example.com",
  "phone": "+33 6 12 34 56 78",
  "mobile": "+33 7 98 76 54 32",
  "address": "12 Rue des Artisans",
  "address2": "Apt 3B",
  "zip_code": "75001",
  "city": "Paris",
  "country": "FR",
  "notes": "Prefers morning appointments.",
  "send_sms": true
}
```

| Field        | Type    | Required | Description |
|--------------|---------|----------|-------------|
| `first_name` | string  | Yes      | First name (max 255). |
| `last_name`  | string  | Yes      | Last name (max 255). |
| `email`      | string  | No       | Email address. |
| `phone`      | string  | No       | Phone number (max 50). |
| `mobile`     | string  | No       | Mobile number (max 50). |
| `address`    | string  | No       | Street address line 1 (max 255). |
| `address2`   | string  | No       | Street address line 2 (max 255). |
| `zip_code`   | string  | No       | Postal code (max 20). |
| `city`       | string  | No       | City (max 255). |
| `country`    | string  | No       | ISO 3166-1 alpha-2 country code (e.g. `"FR"`). |
| `notes`      | string  | No       | Internal notes (max 5000 characters). |
| `send_sms`   | boolean | No       | Whether to include this customer in SMS reminders. |

**Response (201):** the created customer object (same fields as the list response).

#### GET /api/v1/customers/{id}

Retrieve a single customer, including their jobs.

**Response:** same fields as the list response, plus a `jobs` array:

```json
{
  "data": {
    "id": 1,
    "first_name": "Marie",
    "last_name": "Dupont",
    "full_name": "Marie Dupont",
    "email": "marie@example.com",
    "phone": "+33 6 12 34 56 78",
    "mobile": null,
    "address": "12 Rue des Artisans",
    "address2": null,
    "zip_code": "75001",
    "city": "Paris",
    "country": "FR",
    "notes": null,
    "send_sms": true,
    "is_walk_in": false,
    "created_at": "2026-01-15T10:30:00+01:00",
    "updated_at": "2026-01-15T10:30:00+01:00",
    "jobs": [
      { "id": 12, "title": "Bathroom plumbing repair" }
    ]
  }
}
```

#### PUT /api/v1/customers/{id}

Update a customer. Send only the fields you want to change. Accepts the same fields as `POST /api/v1/customers`, all optional.

#### DELETE /api/v1/customers/{id}

Delete a customer. Returns `204 No Content`.

---

### Jobs

#### GET /api/v1/jobs

List all jobs for your company.

**Query parameters:**

| Parameter     | Type    | Description |
|---------------|---------|-------------|
| `customer_id` | integer | Filter by customer ID. |
| `status`      | string  | Filter by status (`draft`, `open`, `in_progress`, `completed`, `cancelled`). |
| `per_page`    | integer | Results per page (default: 25, max: 100). |
| `page`        | integer | Page number (default: 1). |

**Response:**

```json
{
  "data": [
    {
      "id": 12,
      "customer_id": 1,
      "job_type_id": 3,
      "title": "Bathroom plumbing repair",
      "description": "Replace mixer tap and re-seal bath.",
      "address": "12 Rue des Artisans, Paris",
      "status": "in_progress",
      "created_at": "2026-02-01T09:00:00+01:00",
      "updated_at": "2026-02-10T14:30:00+01:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 2,
    "per_page": 25,
    "total": 47
  }
}
```

#### POST /api/v1/jobs

Create a new job record.

| Field         | Type    | Required | Description |
|---------------|---------|----------|-------------|
| `customer_id` | integer | Yes      | ID of the customer this job belongs to. |
| `title`       | string  | Yes      | Job title (max 255). |
| `job_type_id` | integer | No       | ID of the job type. |
| `description` | string  | No       | Job description (max 5000). |
| `address`     | string  | No       | Site address (max 255). |
| `status`      | string  | No       | Initial status: `draft`, `open`, `in_progress`, `completed`, or `cancelled`. |

**Response (201):** the created job object.

#### GET /api/v1/jobs/{id}

Retrieve a single job, including customer details, job type, and site visit count.

**Response:**

```json
{
  "data": {
    "id": 12,
    "customer_id": 1,
    "job_type_id": 3,
    "title": "Bathroom plumbing repair",
    "description": "Replace mixer tap and re-seal bath.",
    "address": "12 Rue des Artisans, Paris",
    "status": "in_progress",
    "created_at": "2026-02-01T09:00:00+01:00",
    "updated_at": "2026-02-10T14:30:00+01:00",
    "customer": {
      "id": 1,
      "full_name": "Marie Dupont"
    },
    "job_type": {
      "id": 3,
      "name": "Plumbing"
    },
    "site_visits_count": 2
  }
}
```

The `job_type.name` field is returned in the locale of the company (`en` or `fr`).

#### PUT /api/v1/jobs/{id}

Update a job. Send only the fields you want to change. Accepts `job_type_id`, `title`, `description`, `address`, and `status`.

#### DELETE /api/v1/jobs/{id}

Delete a job record. Returns `204 No Content`.

---

### Site Visits

#### GET /api/v1/site-visits

List site visits for your company.

**Query parameters:**

| Parameter      | Type    | Description |
|----------------|---------|-------------|
| `from`         | string  | Start date filter (`YYYY-MM-DD`). |
| `to`           | string  | End date filter (`YYYY-MM-DD`). |
| `customer_id`  | integer | Filter by customer ID. |
| `job_id`       | integer | Filter by job ID. |
| `status`       | string  | Filter by status (`scheduled`, `completed`, `cancelled`). |
| `per_page`     | integer | Results per page (default: 25, max: 100). |
| `page`         | integer | Page number. |

**Response:**

```json
{
  "data": [
    {
      "id": 7,
      "job_id": 12,
      "customer_id": 1,
      "team_member_id": 2,
      "scheduled_at": "2026-03-20T09:00:00+01:00",
      "duration_minutes": 120,
      "status": "scheduled",
      "notes": "Customer will be home from 9am.",
      "created_at": "2026-03-01T11:00:00+01:00",
      "updated_at": "2026-03-01T11:00:00+01:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 25,
    "total": 5
  }
}
```

#### POST /api/v1/site-visits

Create a new site visit.

| Field              | Type    | Required | Description |
|--------------------|---------|----------|-------------|
| `job_id`           | integer | Yes      | ID of the job this visit is associated with. |
| `scheduled_at`     | string  | Yes      | Scheduled date and time (ISO 8601, e.g. `"2026-03-20T09:00:00"`). |
| `customer_id`      | integer | No       | Customer ID (defaults to the job's customer). |
| `duration_minutes` | integer | No       | Estimated duration in minutes (min: 1). |
| `notes`            | string  | No       | Internal notes for the visit (max 2000). |
| `status`           | string  | No       | Initial status: `scheduled`, `completed`, or `cancelled`. |
| `team_member_id`   | integer | No       | ID of the team member assigned to this visit. |

**Response (201):** the created site visit object.

#### GET /api/v1/site-visits/{id}

Retrieve a single site visit with job, customer, and team member details.

**Response:**

```json
{
  "data": {
    "id": 7,
    "job_id": 12,
    "customer_id": 1,
    "team_member_id": 2,
    "scheduled_at": "2026-03-20T09:00:00+01:00",
    "duration_minutes": 120,
    "status": "scheduled",
    "notes": "Customer will be home from 9am.",
    "created_at": "2026-03-01T11:00:00+01:00",
    "updated_at": "2026-03-01T11:00:00+01:00",
    "job": {
      "id": 12,
      "title": "Bathroom plumbing repair"
    },
    "customer": {
      "id": 1,
      "full_name": "Marie Dupont"
    },
    "team_member": {
      "id": 2,
      "name": "Jean Martin"
    }
  }
}
```

#### PUT /api/v1/site-visits/{id}

Update a site visit. Send only the fields you want to change. Accepts `scheduled_at`, `duration_minutes`, `notes`, `status`, and `team_member_id`.

#### DELETE /api/v1/site-visits/{id}

Delete a site visit. Returns `204 No Content`.

---

### Invoices

Invoices use an immutable sequential numbering system. For audit integrity, invoices cannot be updated or deleted via the API.

#### GET /api/v1/invoices

List invoices for your company.

**Query parameters:**

| Parameter     | Type    | Description |
|---------------|---------|-------------|
| `from`        | string  | Invoice creation date start (`YYYY-MM-DD`). |
| `to`          | string  | Invoice creation date end (`YYYY-MM-DD`). |
| `customer_id` | integer | Filter by customer ID. |
| `paid`        | boolean | Filter by paid status. |
| `per_page`    | integer | Results per page (default: 25, max: 100). |
| `page`        | integer | Page number. |

**Response:**

```json
{
  "data": [
    {
      "id": 42,
      "invoice_number": "FA260200042",
      "customer_id": 1,
      "paid": true,
      "total_with_tax": 180.00,
      "notes": "Thank you for choosing us!",
      "created_at": "2026-02-15T10:00:00+01:00",
      "updated_at": "2026-02-15T10:00:00+01:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 2,
    "per_page": 25,
    "total": 38
  }
}
```

#### GET /api/v1/invoices/{id}

Retrieve a single invoice with its line items and customer details.

**Response:**

```json
{
  "data": {
    "id": 42,
    "invoice_number": "FA260200042",
    "customer_id": 1,
    "paid": true,
    "total_with_tax": 180.00,
    "notes": "Thank you for choosing us!",
    "created_at": "2026-02-15T10:00:00+01:00",
    "updated_at": "2026-02-15T10:00:00+01:00",
    "customer": {
      "id": 1,
      "full_name": "Marie Dupont",
      "email": "marie@example.com"
    },
    "lines": [
      {
        "id": 101,
        "item_id": 5,
        "description": "Plumbing repair - bathroom",
        "quantity": 1,
        "unit_price_with_tax": 180.00,
        "discount_percent": 0,
        "line_total_with_tax": 180.00
      }
    ]
  }
}
```

#### POST /api/v1/invoices

Create a new invoice. The invoice number is assigned automatically.

**Request body:**

```json
{
  "customer_id": 1,
  "paid": false,
  "notes": "Thank you for choosing us!",
  "lines": [
    {
      "item_id": 5,
      "description": "Plumbing repair - bathroom",
      "quantity": 1,
      "price_with_tax": 180.00,
      "discount": 0
    }
  ]
}
```

| Field                    | Type    | Required | Description |
|--------------------------|---------|----------|-------------|
| `customer_id`            | integer | Yes      | ID of the customer. |
| `paid`                   | boolean | No       | Whether the invoice is already paid (default: `false`). |
| `notes`                  | string  | No       | Notes printed on the invoice (max 5000). |
| `lines`                  | array   | No       | Array of line items. |
| `lines[].item_id`        | integer | No       | ID of the catalog item. |
| `lines[].description`    | string  | No       | Line item description (max 1000). |
| `lines[].quantity`       | number  | Yes*     | Quantity (required when lines are provided, min: 0.01). |
| `lines[].price_with_tax` | number  | Yes*     | Unit price including tax (required when lines are provided, min: 0). |
| `lines[].discount`       | number  | No       | Discount percentage applied to this line (0-50). |

*Required only when the `lines` array is present.

**Response (201):** the created invoice object in the same format as `GET /api/v1/invoices/{id}`.

---

## Error Responses

The API returns standard HTTP status codes. Error responses include a JSON body with an `error` field and an optional `messages` field for validation errors.

| Code | Meaning |
|------|---------|
| 401  | Unauthenticated. Token is missing or invalid. |
| 403  | Forbidden. You do not have access to the requested resource. |
| 404  | Resource not found. |
| 422  | Validation error. Check the `messages` field. |
| 429  | Too many requests. Limit: 60 per minute per token. |
| 500  | Internal server error. |

**Example validation error:**

```json
{
  "error": "Validation failed.",
  "messages": {
    "first_name": ["The first name field is required."],
    "email": ["The email must be a valid email address."]
  }
}
```

---

## Rate Limiting

The API enforces rate limiting to ensure fair use and system stability. Requests are limited to **60 per minute** per API token. If you exceed this limit, you will receive a `429 Too Many Requests` response.

---

## Pagination

All list endpoints return paginated results. The response includes a `meta` object:

```json
{
  "meta": {
    "current_page": 1,
    "last_page": 5,
    "per_page": 25,
    "total": 118
  }
}
```

Use the `page` and `per_page` query parameters to navigate through results.

---

## Getting Started

1. Log in to your Check-in ARTISAN admin panel.
2. Go to **Check-in ARTISAN Account > API Tokens**.
3. Click **Generate token**, give it a name, and copy the token displayed.
4. Use the token in the `Authorization: Bearer` header for all authenticated requests.
5. Test with a simple request to `/api/v1/company` to verify your token works.

---

## Support

If you need help integrating the API, contact us through the **[Contact page](/en/contact)** or consult the full documentation.
