API Reference
Connect your website or external tools to Check-in ARTISAN
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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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
- Log in to your Check-in ARTISAN admin panel.
- Go to Check-in ARTISAN Account > API Tokens.
- Click Generate token, give it a name, and copy the token displayed.
- Use the token in the
Authorization: Bearerheader for all authenticated requests. - Test with a simple request to
/api/v1/companyto verify your token works.
Support
If you need help integrating the API, contact us through the Contact page or consult the full documentation.