Bookings
Services, free times and appointments. A booking made here sends the same emails as the booking page.
GET
/api/v1/services
List the services that can be booked
Works with a public key
Parameters
| Name | In | Type | Required | Description |
lang |
query |
string | null |
No |
en or fr; without it, Accept-Language. |
cursor |
query |
string | null |
No |
|
limit |
query |
integer |
No |
Defaults to the maximum: most businesses fit in one page. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"id": 3,
"name": "Coupe",
"description": "Lavage, coupe et coiffage.",
"duration_min": 45,
"price": "45.00",
"currency": "CAD",
"staff": [
{
"id": 1,
"name": "Julie"
}
],
"image_url": null
}
],
"next_cursor": null
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/services" \
-H "Authorization: Bearer dmk_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
GET
/api/v1/availability
Free start times for a service on one day
Works with a public key
Parameters
| Name | In | Type | Required | Description |
service |
query |
integer |
Yes |
Service id. |
date |
query |
string |
Yes |
Local day, YYYY-MM-DD. |
staff |
query |
integer | null |
No |
Staff id; omit for anyone. |
location |
query |
string | null |
No |
Location id or slug (GET /locations); omit for the primary location. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"start": "2026-09-21T09:00:00-03:00",
"end": "2026-09-21T09:45:00-03:00",
"staff_id": null
}
],
"timezone": "America/Moncton"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/availability?service=7&date=2026-09-21" \
-H "Authorization: Bearer dmk_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
GET
/api/v1/locations
List the locations of the business
Works with a public key
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 429 Rate limited
Example response (200)
{
"data": [
{
"id": 2,
"name": "Downtown",
"slug": "downtown",
"address": "12 King St, Moncton NB",
"phone": "506 555 0100",
"timezone": "America/Moncton",
"primary": true
}
]
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/locations" \
-H "Authorization: Bearer dmk_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/bookings
Book an appointment
Works with a public key
Parameters
| Name | In | Type | Required | Description |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
service |
integer |
Yes |
Service id. |
start |
string |
Yes |
A start from /availability (ISO 8601). |
name |
string |
Yes |
|
email |
string |
Yes |
|
phone |
string | null |
No |
|
staff |
integer | null |
No |
Staff id; omit for anyone. |
location |
integer | null |
No |
Location id (GET /locations); omit for the primary location. |
notes |
string | null |
No |
|
lang |
string | null |
No |
en or fr; defaults to the business language. |
consent |
boolean | null |
No |
Opt-in to news from the business. |
policy_accepted |
boolean | null |
No |
Required when the service asks for a deposit or card. |
Example body
{
"service": 7,
"start": "2026-09-21T09:00:00-03:00",
"name": "Lea Roy",
"email": "lea@example.com",
"phone": "506 555 0100",
"staff": null,
"location": null,
"notes": "First visit",
"lang": "en",
"consent": false,
"policy_accepted": true
}
Responses
- 201 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (201)
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00",
"token": "IfnZPtmgcfpabtYFc_UU4AxdOWc1MHHGjdUU-jrbLAA",
"manage_url": "https://demarkstudio.ca/citas/IfnZPtmgcfpabtYFc_UU4AxdOWc1MHHGjdUU-jrbLAA",
"cancel_url": "https://demarkstudio.ca/citas/IfnZPtmgcfpabtYFc_UU4AxdOWc1MHHGjdUU-jrbLAA/cancel",
"payment_url": null
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/bookings" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"service": 7, "start": "2026-09-21T09:00:00-03:00", "name": "Lea Roy", "email": "lea@example.com", "phone": "506 555 0100", "staff": null, "location": null, "notes": "First visit", "lang": "en", "consent": false, "policy_accepted": true}'
Back to the index
GET
/api/v1/bookings
List bookings
Secret key only
Parameters
| Name | In | Type | Required | Description |
from |
query |
string | null |
No |
Start day or datetime (ISO). |
to |
query |
string | null |
No |
End day (exclusive) or datetime (ISO). |
status |
query |
string | null |
No |
pending, confirmed, cancelled, completed or no_show. |
location |
query |
integer | null |
No |
Only this location's bookings (GET /locations). |
cursor |
query |
string | null |
No |
|
limit |
query |
integer |
No |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00"
}
],
"next_cursor": "MTAx"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/bookings" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
GET
/api/v1/bookings/{booking_id}
Read a booking
Works with a public key
Parameters
| Name | In | Type | Required | Description |
booking_id |
path |
integer |
Yes |
|
token |
query |
string | null |
No |
Management token. Required with a public key or without a key. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/bookings/1" \
-H "Authorization: Bearer dmk_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
DELETE
/api/v1/bookings/{booking_id}
Cancel a booking
Works with a public key
Parameters
| Name | In | Type | Required | Description |
booking_id |
path |
integer |
Yes |
|
token |
query |
string | null |
No |
Management token. Required with a public key or without a key. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 101,
"status": "cancelled"
}
Try it with curl
curl -X DELETE "https://demarkstudio.ca/api/v1/bookings/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
PATCH
/api/v1/bookings/{booking_id}
Reschedule a booking or update its notes
Secret key only
Parameters
| Name | In | Type | Required | Description |
booking_id |
path |
integer |
Yes |
|
Request body
| Name | Type | Required | Description |
start |
string | null |
No |
New start (ISO 8601), from GET /availability/slots. Reschedules the booking: this id is cancelled and a new one is created (see the returned id). Omit to only update notes. |
notes |
string | null |
No |
|
location |
integer | null |
No |
Move the booking to this location (GET /locations). Works like a reschedule: with start, at that time; without it, at the same time. The staff member must work there, the service's room must be one of that location's, and the time must be free there (a staff member's bookings at every location count). |
Example body
{
"start": "2026-09-21T09:00:00-03:00",
"notes": "Client asked to move to the morning",
"location": null
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 101,
"status": "confirmed",
"start": "2026-09-21T09:00:00-03:00",
"end": "2026-09-21T09:45:00-03:00",
"service": {
"id": 7,
"name": "Haircut"
},
"staff": {
"id": 3,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Downtown",
"timezone": "America/Moncton"
},
"customer": {
"name": "Lea Roy",
"email": "lea@example.com",
"phone": ""
},
"notes": "First visit",
"lang": "en",
"source": "api",
"created_at": "2026-09-14T10:02:11-03:00",
"manage_url": "https://demarkstudio.ca/citas/kR3...x9"
}
Try it with curl
curl -X PATCH "https://demarkstudio.ca/api/v1/bookings/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"start": "2026-09-21T09:00:00-03:00", "notes": "Client asked to move to the morning", "location": null}'
Back to the index
GET
/api/v1/availability/slots
Free start times over a date range
Secret key only
Parameters
| Name | In | Type | Required | Description |
service_id |
query |
integer |
Yes |
|
from |
query |
string |
Yes |
First local day, YYYY-MM-DD. |
to |
query |
string |
Yes |
Last local day, YYYY-MM-DD (inclusive). |
staff_id |
query |
integer | null |
No |
|
location |
query |
string | null |
No |
Location id or slug (GET /locations); omit for the primary location. |
Responses
- 200 Successful Response
- 400 Bad request
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"start": "2026-09-21T09:00:00-03:00",
"end": "2026-09-21T09:45:00-03:00",
"staff_id": null
}
],
"timezone": "America/Moncton"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/availability/slots?service_id=1&from=2026-09-21&to=2026-09-25" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/booking-series
Create a recurring series of bookings
Secret key only
Parameters
| Name | In | Type | Required | Description |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
service |
integer |
Yes |
Service id. |
start |
string |
Yes |
First booking (ISO 8601). Every date keeps this local time. |
name |
string |
Yes |
|
email |
string |
No |
|
phone |
string | null |
No |
|
staff |
integer | null |
No |
Staff id; omit to leave it unassigned. |
location |
integer | null |
No |
Location id (GET /locations); omit for the primary location. |
notes |
string | null |
No |
|
lang |
string | null |
No |
en or fr; defaults to the business language. |
notify |
boolean |
No |
Email the customer one message with every date. |
recurrence |
Recurrencia |
Yes |
|
Example body
{
"service": 7,
"start": "2026-10-06T10:00:00-03:00",
"name": "Lea Roy",
"email": "lea@example.com",
"phone": "506 555 0100",
"staff": null,
"location": null,
"notes": "Physio, every two weeks",
"lang": "en",
"notify": true,
"recurrence": {
"frequency": "every_2_weeks",
"count": 6,
"until": null,
"skip_conflicts": true
}
}
Responses
- 201 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (201)
{
"id": 12,
"frequency": "every_2_weeks",
"count": 6,
"until": null,
"created_at": "2026-10-02T09:12:00-03:00",
"bookings": [
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00"
}
],
"skipped": [
{
"start": "2026-10-20T10:00:00-03:00",
"reasons": [
"staff_busy"
]
}
]
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/booking-series" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"service": 7, "start": "2026-10-06T10:00:00-03:00", "name": "Lea Roy", "email": "lea@example.com", "phone": "506 555 0100", "staff": null, "location": null, "notes": "Physio, every two weeks", "lang": "en", "notify": true, "recurrence": {"frequency": "every_2_weeks", "count": 6, "until": null, "skip_conflicts": true}}'
Back to the index
GET
/api/v1/booking-series/{series_id}
Read a recurring series
Secret key only
Parameters
| Name | In | Type | Required | Description |
series_id |
path |
integer |
Yes |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 12,
"frequency": "every_2_weeks",
"count": 6,
"until": null,
"created_at": "2026-10-02T09:12:00-03:00",
"bookings": [
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00"
}
],
"skipped": [
{
"start": "2026-10-20T10:00:00-03:00",
"reasons": [
"staff_busy"
]
}
]
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/booking-series/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/booking-series/{series_id}/cancel
Cancel a series, or one booking and the following ones
Secret key only
Parameters
| Name | In | Type | Required | Description |
series_id |
path |
integer |
Yes |
|
Request body
| Name | Type | Required | Description |
from_booking |
integer | null |
No |
Cancel this booking and the following ones. Omit to cancel every booking of the series that has not started yet. |
notify |
boolean |
No |
Email the customer one message with the cancelled dates. |
Example body
{
"from_booking": null,
"notify": true
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 12,
"cancelled": [
41,
42,
43
]
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/booking-series/1/cancel" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"from_booking": null, "notify": true}'
Back to the index
Customers
The customer list of the business. Secret key only.
GET
/api/v1/customers
List or search customers
Secret key only
Parameters
| Name | In | Type | Required | Description |
email |
query |
string | null |
No |
Exact email. |
q |
query |
string | null |
No |
Search in name, email, company, phone. |
cursor |
query |
string | null |
No |
|
limit |
query |
integer |
No |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"id": 2,
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123",
"company": "",
"lang": "fr",
"notes": "Prefers mornings",
"marketing_consent": false,
"created_at": "2026-10-02T23:24:52-03:00"
}
],
"next_cursor": null
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/customers" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/customers
Add a customer
Secret key only
Parameters
| Name | In | Type | Required | Description |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
name |
string |
Yes |
|
email |
string |
Yes |
|
phone |
string | null |
No |
|
lang |
string | null |
No |
|
consent |
boolean |
Yes |
Must be true: the customer agreed to be stored. |
notes |
string | null |
No |
|
Example body
{
"name": "Lea Roy",
"email": "lea@example.com",
"phone": "506 555 0100",
"lang": "fr",
"consent": true,
"notes": "Met at the fair"
}
Responses
- 200 Already existed
- 201 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 31,
"name": "Lea Roy",
"email": "lea@example.com",
"phone": "506 555 0100",
"company": "",
"lang": "fr",
"notes": "",
"marketing_consent": false,
"created_at": "2026-09-14T10:02:11-03:00",
"created": true
}
Example response (201)
{
"id": 31,
"name": "Lea Roy",
"email": "lea@example.com",
"phone": "506 555 0100",
"company": "",
"lang": "fr",
"notes": "",
"marketing_consent": false,
"created_at": "2026-09-14T10:02:11-03:00",
"created": true
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/customers" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name": "Lea Roy", "email": "lea@example.com", "phone": "506 555 0100", "lang": "fr", "consent": true, "notes": "Met at the fair"}'
Back to the index
GET
/api/v1/customers/{customer_id}
Read a customer
Secret key only
Parameters
| Name | In | Type | Required | Description |
customer_id |
path |
integer |
Yes |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 2,
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123",
"company": "",
"lang": "fr",
"notes": "Prefers mornings",
"marketing_consent": false,
"created_at": "2026-10-02T23:24:52-03:00"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/customers/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
PATCH
/api/v1/customers/{customer_id}
Update a customer
Secret key only
Parameters
| Name | In | Type | Required | Description |
customer_id |
path |
integer |
Yes |
|
Request body
| Name | Type | Required | Description |
name |
string | null |
No |
|
email |
string | null |
No |
422 if empty; 409 if another customer of this business already has it. |
phone |
string | null |
No |
|
lang |
string | null |
No |
en or fr. |
notes |
string | null |
No |
|
Example body
{
"name": "string",
"email": "string",
"phone": "string",
"lang": "string",
"notes": "string"
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 2,
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123",
"company": "",
"lang": "fr",
"notes": "Prefers mornings",
"marketing_consent": false,
"created_at": "2026-10-02T23:24:52-03:00"
}
Try it with curl
curl -X PATCH "https://demarkstudio.ca/api/v1/customers/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name": "string", "email": "string", "phone": "string", "lang": "string", "notes": "string"}'
Back to the index
Products
The shop catalogue: products, options, variants and stock.
POST
/api/v1/products
Create a product
Secret key only
Parameters
| Name | In | Type | Required | Description |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
name_en |
string |
Yes |
|
name_fr |
string | null |
No |
|
description_en |
string | null |
No |
|
description_fr |
string | null |
No |
|
price_cents |
integer |
Yes |
Base price in cents, in the business currency. |
type |
string |
No |
|
stock |
integer | null |
No |
null = unlimited. |
public |
boolean |
No |
false keeps it as a draft: POST /products/{id}/publish shows it. |
option_groups |
array[OptionGroupInput] |
No |
|
variants |
array[VariantInput] |
No |
Settings for some combinations; the others are calculated. |
Example body
{
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"price_cents": 2000,
"type": "physical",
"stock": 10,
"public": false,
"option_groups": [
{
"id": "colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"options": {
"colour": "honey",
"size": "12x12"
},
"sku": "BE-HON-12",
"price_cents": null,
"stock": 4,
"active": true
}
]
}
Responses
- 201 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (201)
{
"id": 9,
"slug": "birds-eye-vinyl",
"name": "Bird's eye vinyl",
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description": "Wall panel.",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"type": "physical",
"price_cents": 2000,
"currency": "CAD",
"stock": 10,
"public": true,
"sold_out": false,
"url": "https://demarkstudio.ca/salon-ana/shop/birds-eye-vinyl",
"images": [
{
"id": 41,
"url": "https://demarkstudio.ca/m/12/41.webp",
"thumbnail_url": "https://demarkstudio.ca/m/12/41_t.webp",
"alt": ""
}
],
"option_groups": [
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
],
"policies": {
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
},
"created_at": "2026-09-14T10:02:11-03:00",
"updated_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/products" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name_en": "Bird's eye vinyl", "name_fr": "Vinyle oeil d'oiseau", "description_en": "Wall panel.", "description_fr": "Panneau mural.", "price_cents": 2000, "type": "physical", "stock": 10, "public": false, "option_groups": [{"id": "colour", "name_en": "Colour", "name_fr": "Couleur", "mode": "surcharge", "options": [null]}], "variants": [{"options": {"colour": "honey", "size": "12x12"}, "sku": "BE-HON-12", "price_cents": null, "stock": 4, "active": true}]}'
Back to the index
GET
/api/v1/products/{product_id}
Read a product with its options and variants
Works with a public key
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
lang |
query |
string | null |
No |
en or fr; without it, Accept-Language. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 9,
"slug": "birds-eye-vinyl",
"name": "Bird's eye vinyl",
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description": "Wall panel.",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"type": "physical",
"price_cents": 2000,
"currency": "CAD",
"stock": 10,
"public": true,
"sold_out": false,
"url": "https://demarkstudio.ca/salon-ana/shop/birds-eye-vinyl",
"images": [
{
"id": 41,
"url": "https://demarkstudio.ca/m/12/41.webp",
"thumbnail_url": "https://demarkstudio.ca/m/12/41_t.webp",
"alt": ""
}
],
"option_groups": [
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
],
"policies": {
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
},
"created_at": "2026-09-14T10:02:11-03:00",
"updated_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/products/1" \
-H "Authorization: Bearer dmk_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
PATCH
/api/v1/products/{product_id}
Update a product
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
Request body
| Name | Type | Required | Description |
name_en |
string | null |
No |
|
name_fr |
string | null |
No |
|
description_en |
string | null |
No |
|
description_fr |
string | null |
No |
|
price_cents |
integer | null |
No |
|
public |
boolean | null |
No |
|
stock |
integer | null |
No |
Absolute product stock; null = unlimited. Not together with stock_delta. |
stock_delta |
integer | null |
No |
Refused with 422 insufficient_stock if it would go below 0. |
Example body
{
"name_en": "Walnut vinyl",
"name_fr": null,
"description_en": null,
"description_fr": null,
"price_cents": 2200,
"public": true,
"stock": null,
"stock_delta": 5
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 9,
"slug": "birds-eye-vinyl",
"name": "Bird's eye vinyl",
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description": "Wall panel.",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"type": "physical",
"price_cents": 2000,
"currency": "CAD",
"stock": 10,
"public": true,
"sold_out": false,
"url": "https://demarkstudio.ca/salon-ana/shop/birds-eye-vinyl",
"images": [
{
"id": 41,
"url": "https://demarkstudio.ca/m/12/41.webp",
"thumbnail_url": "https://demarkstudio.ca/m/12/41_t.webp",
"alt": ""
}
],
"option_groups": [
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
],
"policies": {
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
},
"created_at": "2026-09-14T10:02:11-03:00",
"updated_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X PATCH "https://demarkstudio.ca/api/v1/products/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name_en": "Walnut vinyl", "name_fr": null, "description_en": null, "description_fr": null, "price_cents": 2200, "public": true, "stock": null, "stock_delta": 5}'
Back to the index
POST
/api/v1/products/{product_id}/publish
Publish a product in the shop
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 9,
"slug": "birds-eye-vinyl",
"name": "Bird's eye vinyl",
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description": "Wall panel.",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"type": "physical",
"price_cents": 2000,
"currency": "CAD",
"stock": 10,
"public": true,
"sold_out": false,
"url": "https://demarkstudio.ca/salon-ana/shop/birds-eye-vinyl",
"images": [
{
"id": 41,
"url": "https://demarkstudio.ca/m/12/41.webp",
"thumbnail_url": "https://demarkstudio.ca/m/12/41_t.webp",
"alt": ""
}
],
"option_groups": [
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
],
"policies": {
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
},
"created_at": "2026-09-14T10:02:11-03:00",
"updated_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/products/1/publish" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/products/{product_id}/unpublish
Take a product out of the shop
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 9,
"slug": "birds-eye-vinyl",
"name": "Bird's eye vinyl",
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description": "Wall panel.",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"type": "physical",
"price_cents": 2000,
"currency": "CAD",
"stock": 10,
"public": true,
"sold_out": false,
"url": "https://demarkstudio.ca/salon-ana/shop/birds-eye-vinyl",
"images": [
{
"id": 41,
"url": "https://demarkstudio.ca/m/12/41.webp",
"thumbnail_url": "https://demarkstudio.ca/m/12/41_t.webp",
"alt": ""
}
],
"option_groups": [
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
],
"policies": {
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
},
"created_at": "2026-09-14T10:02:11-03:00",
"updated_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/products/1/unpublish" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/products/{product_id}/option-groups
Add an option group to a product
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
id |
string | null |
No |
Group id (slug). Omit it to derive it from name_en. |
name_en |
string |
Yes |
|
name_fr |
string | null |
No |
Defaults to name_en. |
mode |
string |
No |
surcharge adds amount_cents; price sets the price. At most one price group per product. |
options |
array[OptionInput] |
Yes |
|
Example body
{
"id": "colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
{
"id": "honey",
"name_en": "Honey maple",
"name_fr": "Erable miel",
"amount_cents": 300,
"image_id": null
}
]
}
Responses
- 201 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (201)
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
{
"id": "honey",
"name": "Honey maple",
"name_en": "Honey maple",
"name_fr": "Erable miel",
"amount_cents": null,
"image_id": null,
"image_url": null
}
]
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/products/1/option-groups" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id": "colour", "name_en": "Colour", "name_fr": "Couleur", "mode": "surcharge", "options": [{"id": "honey", "name_en": "Honey maple", "name_fr": "Erable miel", "amount_cents": 300, "image_id": null}]}'
Back to the index
PATCH
/api/v1/products/{product_id}/option-groups/{group_id}
Update an option group
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
group_id |
path |
string |
Yes |
Option group id. |
Request body
| Name | Type | Required | Description |
name_en |
string | null |
No |
|
name_fr |
string | null |
No |
|
mode |
string | null |
No |
|
options |
array[OptionInput] | null |
No |
Replaces the whole list. Variants of option ids that disappear are dropped; the others keep sku, price and stock. |
Example body
{
"name_en": "Finish",
"name_fr": "Fini",
"mode": "price",
"options": [
{
"id": "honey",
"name_en": "Honey maple",
"name_fr": "Erable miel",
"amount_cents": 300,
"image_id": null
}
]
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
{
"id": "honey",
"name": "Honey maple",
"name_en": "Honey maple",
"name_fr": "Erable miel",
"amount_cents": null,
"image_id": null,
"image_url": null
}
]
}
Try it with curl
curl -X PATCH "https://demarkstudio.ca/api/v1/products/1/option-groups/string" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name_en": "Finish", "name_fr": "Fini", "mode": "price", "options": [{"id": "honey", "name_en": "Honey maple", "name_fr": "Erable miel", "amount_cents": 300, "image_id": null}]}'
Back to the index
DELETE
/api/v1/products/{product_id}/option-groups/{group_id}
Remove an option group
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
group_id |
path |
string |
Yes |
Option group id. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 9,
"slug": "birds-eye-vinyl",
"name": "Bird's eye vinyl",
"name_en": "Bird's eye vinyl",
"name_fr": "Vinyle oeil d'oiseau",
"description": "Wall panel.",
"description_en": "Wall panel.",
"description_fr": "Panneau mural.",
"type": "physical",
"price_cents": 2000,
"currency": "CAD",
"stock": 10,
"public": true,
"sold_out": false,
"url": "https://demarkstudio.ca/salon-ana/shop/birds-eye-vinyl",
"images": [
{
"id": 41,
"url": "https://demarkstudio.ca/m/12/41.webp",
"thumbnail_url": "https://demarkstudio.ca/m/12/41_t.webp",
"alt": ""
}
],
"option_groups": [
{
"id": "colour",
"name": "Colour",
"name_en": "Colour",
"name_fr": "Couleur",
"mode": "surcharge",
"options": [
null
]
}
],
"variants": [
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
],
"policies": {
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
},
"created_at": "2026-09-14T10:02:11-03:00",
"updated_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X DELETE "https://demarkstudio.ca/api/v1/products/1/option-groups/string" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/products/{product_id}/variants
Set up a variant
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
options |
object |
Yes |
One option per group: {group id: option id}. |
sku |
string | null |
No |
|
price_cents |
integer | null |
No |
Fixed price; null = calculated from the product price and the option amounts. |
stock |
integer | null |
No |
Own stock; null = shares the product stock. |
active |
boolean |
No |
An inactive variant is kept but cannot be bought. |
Example body
{
"options": {
"colour": "honey",
"size": "12x12"
},
"sku": "BE-HON-12",
"price_cents": null,
"stock": 4,
"active": true
}
Responses
- 201 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (201)
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/products/1/variants" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"options": {"colour": "honey", "size": "12x12"}, "sku": "BE-HON-12", "price_cents": null, "stock": 4, "active": true}'
Back to the index
PATCH
/api/v1/products/{product_id}/variants/{variant_id}
Update a variant (price, stock, active)
Secret key only
Parameters
| Name | In | Type | Required | Description |
product_id |
path |
integer |
Yes |
Product id. |
variant_id |
path |
string |
Yes |
Variant id, '|' URL-encoded as %7C. |
Request body
| Name | Type | Required | Description |
sku |
string | null |
No |
|
price_cents |
integer | null |
No |
null = calculated. |
stock |
integer | null |
No |
Absolute own stock; null = shares the product stock. Not together with stock_delta. |
stock_delta |
integer | null |
No |
Adds to the stock the variant uses (its own or the shared one). Refused with 422 insufficient_stock if it would go below 0. |
active |
boolean | null |
No |
|
Example body
{
"sku": "BE-HON-12",
"price_cents": 2500,
"stock": null,
"stock_delta": -2,
"active": false
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": "honey|12x12",
"sku": "BE-HON-12",
"price_cents": 1500,
"stock": 4,
"shared_stock": false,
"options": {
"colour": "honey",
"size": "12x12"
},
"active": true,
"available": true
}
Try it with curl
curl -X PATCH "https://demarkstudio.ca/api/v1/products/1/variants/string" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"sku": "BE-HON-12", "price_cents": 2500, "stock": null, "stock_delta": -2, "active": false}'
Back to the index
GET
/api/v1/shop/policies
Read the shipping and returns policies
Works with a public key
Parameters
| Name | In | Type | Required | Description |
lang |
query |
string | null |
No |
en or fr. |
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"lang": "en",
"shipping": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
},
"returns": {
"text": "We ship across New Brunswick for $12.00.",
"paragraphs": [
"We ship across New Brunswick for $12.00."
],
"url": "https://demarkstudio.ca/salon-ana/shop/shipping"
}
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/shop/policies" \
-H "Authorization: Bearer dmk_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
Orders
Shop orders and their status.
GET
/api/v1/orders
List orders
Secret key only
Parameters
| Name | In | Type | Required | Description |
status |
query |
string | null |
No |
Only orders in this status (pending_payment, paid, preparing, ready, shipped, delivered, cancelled, refunded, paid_after_cancel). |
since |
query |
string | null |
No |
Created at or after this ISO date or datetime. |
cursor |
query |
string | null |
No |
|
limit |
query |
integer |
No |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"id": 18,
"number": "O-2026-0018",
"status": "pending_payment",
"customer": {
"name": null,
"email": null,
"phone": null
},
"delivery": "pickup",
"subtotal": "40.00",
"shipping": "0.00",
"tax": "6.00",
"total": "46.00",
"currency": "CAD",
"items": [
null
],
"created_at": "2026-09-14T10:02:11-03:00",
"customer_id": 31,
"subtotal_cents": 4000,
"shipping_cents": 0,
"tax_cents": 600,
"taxes": [
null
],
"total_cents": 4600,
"lines": [
null
],
"allowed_transitions": [
"paid",
"cancelled"
]
}
],
"next_cursor": null
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/orders" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
GET
/api/v1/orders/{order_id}
Read an order
Secret key only
Parameters
| Name | In | Type | Required | Description |
order_id |
path |
integer |
Yes |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 18,
"number": "O-2026-0018",
"status": "pending_payment",
"customer": {
"name": "Lea Roy",
"email": "lea@example.com",
"phone": ""
},
"delivery": "pickup",
"subtotal": "40.00",
"shipping": "0.00",
"tax": "6.00",
"total": "46.00",
"currency": "CAD",
"items": [
{
"product_id": 9,
"name": "Cedar candle",
"price": "20.00",
"qty": 2
}
],
"created_at": "2026-09-14T10:02:11-03:00",
"customer_id": 31,
"subtotal_cents": 4000,
"shipping_cents": 0,
"tax_cents": 600,
"taxes": [
{
"name": "GST",
"rate": 5.0,
"registration_number": "123456789 RT0001",
"amount_cents": 300
}
],
"total_cents": 4600,
"lines": [
{
"product_id": 9,
"variant_id": "honey|12x12",
"name": "Bird's eye vinyl",
"options": {
"colour": "honey",
"size": "12x12"
},
"options_label": "Colour: Honey maple, Size: 12 x 12 in",
"qty": 2,
"unit_price_cents": 1500,
"total_cents": 3000
}
],
"allowed_transitions": [
"paid",
"cancelled"
],
"history": [
{
"status": "paid",
"at": "2026-09-14T10:05:00-03:00",
"source": "system"
}
]
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/orders/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
PATCH
/api/v1/orders/{order_id}/status
Change the status of an order
Secret key only
Parameters
| Name | In | Type | Required | Description |
order_id |
path |
integer |
Yes |
|
Request body
| Name | Type | Required | Description |
status |
string |
Yes |
preparing, ready, shipped, delivered, cancelled, refunded, or paid (only with manual_payment). |
manual_payment |
boolean |
No |
Required (true) to mark an order paid: payment taken outside DeMark. Audited. |
tracking |
string | null |
No |
Tracking number for shipped. |
Example body
{
"status": "shipped",
"manual_payment": false,
"tracking": "CA123456789"
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, scope or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 18,
"number": "O-2026-0018",
"status": "pending_payment",
"customer": {
"name": "Lea Roy",
"email": "lea@example.com",
"phone": ""
},
"delivery": "pickup",
"subtotal": "40.00",
"shipping": "0.00",
"tax": "6.00",
"total": "46.00",
"currency": "CAD",
"items": [
{
"product_id": 9,
"name": "Cedar candle",
"price": "20.00",
"qty": 2
}
],
"created_at": "2026-09-14T10:02:11-03:00",
"customer_id": 31,
"subtotal_cents": 4000,
"shipping_cents": 0,
"tax_cents": 600,
"taxes": [
{
"name": "GST",
"rate": 5.0,
"registration_number": "123456789 RT0001",
"amount_cents": 300
}
],
"total_cents": 4600,
"lines": [
{
"product_id": 9,
"variant_id": "honey|12x12",
"name": "Bird's eye vinyl",
"options": {
"colour": "honey",
"size": "12x12"
},
"options_label": "Colour: Honey maple, Size: 12 x 12 in",
"qty": 2,
"unit_price_cents": 1500,
"total_cents": 3000
}
],
"allowed_transitions": [
"paid",
"cancelled"
],
"history": [
{
"status": "paid",
"at": "2026-09-14T10:05:00-03:00",
"source": "system"
}
]
}
Try it with curl
curl -X PATCH "https://demarkstudio.ca/api/v1/orders/1/status" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"status": "shipped", "manual_payment": false, "tracking": "CA123456789"}'
Back to the index
Invoices
Invoices and their payments.
GET
/api/v1/invoices
List invoices
Secret key only
Parameters
| Name | In | Type | Required | Description |
cursor |
query |
string | null |
No |
|
limit |
query |
integer |
No |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"data": [
{
"id": 42,
"number": "2026-0042",
"status": "issued",
"customer": {
"name": null,
"email": null,
"phone": null
},
"currency": "CAD",
"base": "1200.00",
"tax": "180.00",
"taxes": [
null
],
"total": "1380.00",
"due_date": "2026-10-14",
"paid_at": null,
"lines": [
null
],
"payments": [
null
],
"created_at": "2026-09-14T10:02:11-03:00"
}
],
"next_cursor": null
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/invoices" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
GET
/api/v1/invoices/{invoice_id}
Read an invoice
Secret key only
Parameters
| Name | In | Type | Required | Description |
invoice_id |
path |
integer |
Yes |
|
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 42,
"number": "2026-0042",
"status": "issued",
"customer": {
"name": "Lea Roy",
"email": "lea@example.com",
"phone": ""
},
"currency": "CAD",
"base": "1200.00",
"tax": "180.00",
"taxes": [
{
"name": "GST",
"rate": 5.0,
"registration_number": "123456789 RT0001",
"amount": "60.00"
}
],
"total": "1380.00",
"due_date": "2026-10-14",
"paid_at": null,
"lines": [
{
"concept": "Kitchen cabinets",
"detail": "",
"qty": 1.0,
"unit_price": "1200.00",
"amount": "1200.00",
"tax_exempt": false
}
],
"payments": [
{
"provider": "stripe",
"amount": "1380.00",
"currency": "CAD",
"status": "paid",
"reference": "pi_3Nk...",
"created_at": "2026-09-14T10:02:11-03:00"
}
],
"created_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X GET "https://demarkstudio.ca/api/v1/invoices/1" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Back to the index
POST
/api/v1/invoices/{invoice_id}/mark-paid
Mark an invoice as paid
Secret key only
Parameters
| Name | In | Type | Required | Description |
invoice_id |
path |
integer |
Yes |
|
Idempotency-Key |
header |
string |
No |
Retry safely: the first 2xx response is kept 24 hours and replayed with Idempotent-Replayed: true. Same key with a different body: 422 idempotency_key_reused. |
Request body
| Name | Type | Required | Description |
method |
string |
Yes |
|
reference |
string | null |
No |
|
paid_at |
string | null |
No |
ISO 8601; defaults to now. |
Example body
{
"method": "e-transfer",
"reference": "ET-88213",
"paid_at": null
}
Responses
- 200 Successful Response
- 401 Missing, invalid or revoked API key
- 403 Forbidden: key type, permission, origin or plan
- 404 Not found
- 409 Conflict
- 422 Validation error
- 429 Rate limited
Example response (200)
{
"id": 42,
"number": "2026-0042",
"status": "issued",
"customer": {
"name": "Lea Roy",
"email": "lea@example.com",
"phone": ""
},
"currency": "CAD",
"base": "1200.00",
"tax": "180.00",
"taxes": [
{
"name": "GST",
"rate": 5.0,
"registration_number": "123456789 RT0001",
"amount": "60.00"
}
],
"total": "1380.00",
"due_date": "2026-10-14",
"paid_at": null,
"lines": [
{
"concept": "Kitchen cabinets",
"detail": "",
"qty": 1.0,
"unit_price": "1200.00",
"amount": "1200.00",
"tax_exempt": false
}
],
"payments": [
{
"provider": "stripe",
"amount": "1380.00",
"currency": "CAD",
"status": "paid",
"reference": "pi_3Nk...",
"created_at": "2026-09-14T10:02:11-03:00"
}
],
"created_at": "2026-09-14T10:02:11-03:00"
}
Try it with curl
curl -X POST "https://demarkstudio.ca/api/v1/invoices/1/mark-paid" \
-H "Authorization: Bearer dmk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"method": "e-transfer", "reference": "ET-88213", "paid_at": null}'
Back to the index
Models
The objects the API returns and the webhooks send. Dates carry your business offset, money is a decimal string and every value is the public English one.
Booking
Fields of Booking
| Name | Type | Required | Description |
id |
integer |
Yes |
|
status |
string |
Yes |
pending, confirmed, cancelled, completed or no_show. |
start |
string |
Yes |
ISO 8601 with the business offset. |
end |
string |
Yes |
|
service |
ServiceRef | null |
Yes |
null when the service was deleted. |
staff |
StaffRef | null |
Yes |
null when anyone can do it. |
location |
LocationRef | null |
Yes |
Where it takes place (GET /locations). start and end carry the business offset; the location time zone is here. |
customer |
CustomerRef |
Yes |
|
notes |
string | null |
Yes |
|
lang |
string | null |
Yes |
en or fr: the language of the booking and its emails. |
source |
string | null |
Yes |
Where it was made: web, api, panel, portal... |
created_at |
string | null |
Yes |
|
Real example
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00"
}
NewBooking
Fields of NewBooking
| Name | Type | Required | Description |
id |
integer |
Yes |
|
status |
string |
Yes |
pending, confirmed, cancelled, completed or no_show. |
start |
string |
Yes |
ISO 8601 with the business offset. |
end |
string |
Yes |
|
service |
ServiceRef | null |
Yes |
null when the service was deleted. |
staff |
StaffRef | null |
Yes |
null when anyone can do it. |
location |
LocationRef | null |
Yes |
Where it takes place (GET /locations). start and end carry the business offset; the location time zone is here. |
customer |
CustomerRef |
Yes |
|
notes |
string | null |
Yes |
|
lang |
string | null |
Yes |
en or fr: the language of the booking and its emails. |
source |
string | null |
Yes |
Where it was made: web, api, panel, portal... |
created_at |
string | null |
Yes |
|
token |
string |
Yes |
Management token. Shown once: store it to read or cancel the booking. |
manage_url |
string |
Yes |
Page where the visitor manages the booking. |
cancel_url |
string |
Yes |
Page where the visitor confirms the cancellation (GET, no key). Servers cancel with DELETE /bookings/{id}?token=... or with a secret key. |
payment_url |
string | null |
Yes |
Deposit or card checkout, when the service asks for it. |
Real example
{
"id": 1,
"status": "confirmed",
"start": "2026-10-09T09:00:00-03:00",
"end": "2026-10-09T09:45:00-03:00",
"service": {
"id": 3,
"name": "Coupe"
},
"staff": {
"id": 1,
"name": "Julie"
},
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"customer": {
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123"
},
"notes": "First visit",
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00",
"token": "IfnZPtmgcfpabtYFc_UU4AxdOWc1MHHGjdUU-jrbLAA",
"manage_url": "https://demarkstudio.ca/citas/IfnZPtmgcfpabtYFc_UU4AxdOWc1MHHGjdUU-jrbLAA",
"cancel_url": "https://demarkstudio.ca/citas/IfnZPtmgcfpabtYFc_UU4AxdOWc1MHHGjdUU-jrbLAA/cancel",
"payment_url": null
}
Request
Fields of Request
| Name | Type | Required | Description |
id |
integer |
Yes |
|
type |
string |
Yes |
quote, contact or support (also lead, booking, question, event, table_booking, other for requests that came from other channels). |
status |
string |
Yes |
new, open, waiting_customer, resolved or closed. |
subject |
string |
Yes |
|
message |
string |
Yes |
What the customer wrote, without page or file lines. |
customer |
CustomerRef |
Yes |
|
page_url |
string | null |
Yes |
Page the request was sent from, when known. |
attachments |
array[Attachment] |
Yes |
Empty when there are none. |
lang |
string | null |
Yes |
|
source |
string |
Yes |
api, web, site, chat, email... |
created_at |
string |
Yes |
|
Real example
{
"id": 1,
"type": "quote",
"status": "new",
"subject": "Wedding party, 6 people",
"message": "Hair and make-up for six people on a Saturday morning.",
"customer": {
"name": "Marc Leblanc",
"email": "marc.leblanc@mail.test",
"phone": "506 555 0199"
},
"page_url": "https://www.your-salon.ca/weddings",
"attachments": [
{
"id": 1,
"filename": "inspiration.png",
"content_type": "image/png",
"size": 69,
"url": "https://demarkstudio.ca/adjuntos/2/1?exp=1791253492&sig=c376ff521d8e24efa9fa372d1b46bd445c99acfc839ccd051a54d04c9dff4c3f",
"expires_at": "2026-10-05T23:24:52-03:00"
}
],
"lang": "fr",
"source": "api",
"created_at": "2026-10-02T23:24:52-03:00"
}
Attachment
Fields of Attachment
| Name | Type | Required | Description |
id |
integer |
Yes |
|
filename |
string |
Yes |
|
content_type |
string |
Yes |
|
size |
integer |
Yes |
Bytes. |
url |
string |
Yes |
Signed download link; no key needed until expires_at. |
expires_at |
string |
Yes |
ISO 8601; ask for the request again to get a fresh url. |
Real example
{
"id": 1,
"filename": "inspiration.png",
"content_type": "image/png",
"size": 69,
"url": "https://demarkstudio.ca/adjuntos/2/1?exp=1791253492&sig=c376ff521d8e24efa9fa372d1b46bd445c99acfc839ccd051a54d04c9dff4c3f",
"expires_at": "2026-10-05T23:24:52-03:00"
}
Customer
Fields of Customer
| Name | Type | Required | Description |
id |
integer |
Yes |
|
name |
string |
Yes |
|
email |
string |
Yes |
|
phone |
string |
Yes |
|
company |
string |
Yes |
|
lang |
string |
Yes |
|
notes |
string |
Yes |
|
marketing_consent |
boolean |
Yes |
|
created_at |
string | null |
Yes |
|
Real example
{
"id": 2,
"name": "Marie Tremblay",
"email": "marie.tremblay@mail.test",
"phone": "506 555 0123",
"company": "",
"lang": "fr",
"notes": "Prefers mornings",
"marketing_consent": false,
"created_at": "2026-10-02T23:24:52-03:00"
}
Order
Fields of Order
| Name | Type | Required | Description |
id |
integer |
Yes |
|
number |
string |
Yes |
|
status |
string |
Yes |
pending_payment, paid, preparing, ready, shipped, delivered, cancelled, refunded or paid_after_cancel. |
customer |
CustomerRef |
Yes |
|
delivery |
string |
Yes |
pickup, shipping, digital or empty. |
subtotal |
string |
Yes |
|
shipping |
string |
Yes |
|
tax |
string |
Yes |
|
total |
string |
Yes |
|
currency |
string |
Yes |
|
items |
array[OrderItem] |
Yes |
|
created_at |
string |
Yes |
|
Real example
{
"id": 1,
"number": "O-2026-0001",
"status": "paid",
"customer": {
"name": "Lea Roy",
"email": "lea.roy@mail.test",
"phone": ""
},
"delivery": "pickup",
"subtotal": "40.00",
"shipping": "0.00",
"tax": "0.00",
"total": "40.00",
"currency": "CAD",
"items": [
{
"product_id": 1,
"name": "Cedar candle",
"price": "20.00",
"qty": 2
}
],
"created_at": "2026-10-02T23:24:52-03:00"
}
Event
Fields of Event
| Name | Type | Required | Description |
id |
string |
Yes |
Occurrence id: <event_id>-<YYYY-MM-DD>. |
event_id |
integer |
Yes |
|
title |
string |
Yes |
In the requested language. |
description |
string |
Yes |
|
start |
string |
Yes |
|
end |
string |
Yes |
|
seats_left |
integer | null |
Yes |
null = no capacity limit. |
price |
string | null |
Yes |
|
currency |
string |
Yes |
|
url |
string |
Yes |
Event page in the requested language. |
Real example
{
"id": "1-2026-10-14",
"event_id": 1,
"title": "Atelier de soins capillaires",
"description": "Deux heures pour apprendre une routine qui dure.",
"start": "2026-10-14T18:00:00-03:00",
"end": "2026-10-14T20:00:00-03:00",
"seats_left": 12,
"price": "25.00",
"currency": "CAD",
"url": "https://demarkstudio.ca/salon-ana/events/hair-care-workshop?ocurrencia=2026-10-14&lang=fr"
}
Service
Fields of Service
| Name | Type | Required | Description |
id |
integer |
Yes |
|
name |
string |
Yes |
In the requested language. |
description |
string |
Yes |
|
duration_min |
integer |
Yes |
|
price |
string | null |
Yes |
Decimal as string, or null when the service has no price. |
currency |
string |
Yes |
|
staff |
array[StaffRef] |
Yes |
Who can do it; empty when the business has no team. |
image_url |
string | null |
Yes |
|
Real example
{
"id": 3,
"name": "Coupe",
"description": "Lavage, coupe et coiffage.",
"duration_min": 45,
"price": "45.00",
"currency": "CAD",
"staff": [
{
"id": 1,
"name": "Julie"
}
],
"image_url": null
}
Product
Fields of Product
| Name | Type | Required | Description |
id |
integer |
Yes |
|
name |
string |
Yes |
|
description |
string |
Yes |
|
price |
string | null |
Yes |
|
currency |
string |
Yes |
|
image_url |
string | null |
Yes |
|
url |
string |
Yes |
Product page in the requested language. |
stock |
integer | null |
Yes |
null = unlimited. |
variants |
array[object] |
Yes |
Active variants (id, sku, price_cents, stock, options, active, available); empty when the product has no options. |
Real example
{
"id": 1,
"name": "Bougie au cedre",
"description": "",
"price": "20.00",
"currency": "CAD",
"image_url": null,
"url": "https://demarkstudio.ca/salon-ana/shop/cedar-candle?lang=fr",
"stock": 10,
"variants": []
}
Webhook bodies
Each body below was generated by DeMark itself from a test booking, request, order and customer. data is the same model the REST API returns, so one class in your code reads both.
Version 1 only adds fields: ignore the ones you do not know. created is kept as a UTC alias of created_at for older receivers.
POST booking.created
A booking is confirmed, whether it came from a widget, the API, your booking page or the back office. data is a WebhookBooking
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.567Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"created_at": "2026-10-02T23:24:52-03:00",
"customer": {
"email": "marie.tremblay@mail.test",
"name": "Marie Tremblay",
"phone": "506 555 0123"
},
"end": "2026-10-09T09:45:00-03:00",
"id": 1,
"lang": "fr",
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"manage_url": "https://demarkstudio.ca/citas/c2-1-12f82226c4db44f694f2343dd1942d94",
"notes": "First visit",
"service": {
"id": 3,
"name": "Coupe"
},
"source": "api",
"staff": {
"id": 1,
"name": "Julie"
},
"start": "2026-10-09T09:00:00-03:00",
"status": "confirmed"
},
"id": "evt_2",
"type": "booking.created"
}
Back to the index
POST booking.cancelled
A booking is cancelled, by the customer or by you. data is a WebhookBooking
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.609Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"created_at": "2026-10-02T23:24:52-03:00",
"customer": {
"email": "luc.arsenault@mail.test",
"name": "Luc Arsenault",
"phone": ""
},
"end": "2026-10-09T10:45:00-03:00",
"id": 2,
"lang": "en",
"location": {
"id": 2,
"name": "Salon Ana",
"timezone": "America/Moncton"
},
"manage_url": "https://demarkstudio.ca/citas/c2-2-dcd2cbc1d9ca3db5593cd8364bf6349b",
"notes": "",
"service": {
"id": 3,
"name": "Haircut"
},
"source": "api",
"staff": {
"id": 1,
"name": "Julie"
},
"start": "2026-10-09T10:00:00-03:00",
"status": "cancelled"
},
"id": "evt_5",
"type": "booking.cancelled"
}
Back to the index
POST request.created
A quote, contact or support request arrives. data is a Request
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.624Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"attachments": [
{
"content_type": "image/png",
"expires_at": "2026-10-05T23:24:52-03:00",
"filename": "inspiration.png",
"id": 1,
"size": 69,
"url": "https://demarkstudio.ca/adjuntos/2/1?exp=1791253492&sig=c376ff521d8e24efa9fa372d1b46bd445c99acfc839ccd051a54d04c9dff4c3f"
}
],
"created_at": "2026-10-02T23:24:52-03:00",
"customer": {
"email": "marc.leblanc@mail.test",
"name": "Marc Leblanc",
"phone": "506 555 0199"
},
"id": 1,
"lang": "fr",
"message": "Hair and make-up for six people on a Saturday morning.",
"page_url": "https://www.your-salon.ca/weddings",
"source": "api",
"status": "new",
"subject": "Wedding party, 6 people",
"type": "quote"
},
"id": "evt_7",
"type": "request.created"
}
Back to the index
POST order.paid
An order is paid. data is a Order
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.652Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"created_at": "2026-10-02T23:24:52-03:00",
"currency": "CAD",
"customer": {
"email": "lea.roy@mail.test",
"name": "Lea Roy",
"phone": ""
},
"delivery": "pickup",
"id": 1,
"items": [
{
"name": "Cedar candle",
"price": "20.00",
"product_id": 1,
"qty": 2
}
],
"number": "O-2026-0001",
"shipping": "0.00",
"status": "paid",
"subtotal": "40.00",
"tax": "0.00",
"total": "40.00"
},
"id": "evt_10",
"type": "order.paid"
}
Back to the index
POST customer.created
A new customer is added. data is a Customer
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.552Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"company": "",
"created_at": "2026-10-02T23:24:52-03:00",
"email": "marie.tremblay@mail.test",
"id": 2,
"lang": "fr",
"marketing_consent": false,
"name": "Marie Tremblay",
"notes": "Prefers mornings",
"phone": "506 555 0123"
},
"id": "evt_1",
"type": "customer.created"
}
Back to the index
POST order.created
An order is placed, before it is paid. data is a Order
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.645Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"created_at": "2026-10-02T23:24:52-03:00",
"currency": "CAD",
"customer": {
"email": "lea.roy@mail.test",
"name": "Lea Roy",
"phone": ""
},
"delivery": "pickup",
"id": 1,
"items": [
{
"name": "Cedar candle",
"price": "20.00",
"product_id": 1,
"qty": 2
}
],
"number": "O-2026-0001",
"shipping": "0.00",
"status": "pending_payment",
"subtotal": "40.00",
"tax": "0.00",
"total": "40.00"
},
"id": "evt_9",
"type": "order.created"
}
Back to the index
POST invoice.paid
An invoice is marked as paid. data is a WebhookInvoice
{
"business": {
"id": 2,
"slug": "salon-ana"
},
"created": "2026-10-03T02:24:52.731Z",
"created_at": "2026-10-02T23:24:52-03:00",
"data": {
"base": "510.00",
"created_at": "2026-10-02T23:24:52-03:00",
"currency": "CAD",
"customer": {
"email": "marie.tremblay@mail.test",
"name": "Marie Tremblay",
"phone": null
},
"due_date": "2026-11-01",
"id": 1,
"lines": [
{
"amount": "510.00",
"concept": "Wedding party",
"detail": "Hair and make-up, 6 people",
"qty": 6.0,
"tax_exempt": false,
"unit_price": "85.00"
}
],
"number": "2026-0001",
"paid_at": "2026-10-02T23:24:52-03:00",
"status": "paid",
"tax": "76.37",
"taxes": [
{
"amount": "25.50",
"name": "GST",
"rate": 5.0,
"registration_number": "123456789 RT0001"
},
{
"amount": "50.87",
"name": "QST",
"rate": 9.975,
"registration_number": "1234567890 TQ0001"
}
],
"total": "586.37"
},
"id": "evt_11",
"type": "invoice.paid"
}
Back to the index