Docfav Public API
Welcome to the Docfav Public API. This API allows you to integrate your system with Docfav to manage appointments, clients, availability, and more.
https://www.docfav.com/api-connector/v1Response Format
All responses follow this structure:
{"data": { ... },
"errors": [],
"meta": {"timestamp": "2026-02-05T12:00:00+00:00",
"api_version": "v1"
}
}Timestamps
All datetime values in the API are in UTC (ISO 8601 format). Use the timezone field from staff calendars to convert to local time when displaying to users.
Authentication
The API uses API Key authentication. Include your API key in the X-Api-Key header of each request.
curl -H "X-Api-Key: df_pk_your_api_key" \
https://www.docfav.com/api-connector/v1/meGetting your API Key
- Log in to your Docfav account
- Go to Settings → API
- Create a new API Key
- Copy and save the key securely (it's only shown once)
Scopes
API keys have specific permissions (scopes) that determine which endpoints they can access:
| Scope | Description |
|---|---|
user:read | Access to user information |
profiles:read | Access to professional profiles |
backofficeclinics:read | Access to back office clinic information |
staff:read | Access to staff and staff calendars information |
services:read | Access to services catalog |
clients:read | Access to client information and search |
clients:write | Create and update clients |
appointments:read | Access to appointments and availability |
appointments:write | Create and update appointments |
sales:read | Access to sales information |
products:read | Access to sold products (sale line items) |
payments:read | Access to payments and refunds (cash-flow movements) |
invoices:read | Access to invoices, rectified invoices, PDFs, TicketBai XML and invoice issuers |
Rate Limiting
API requests are rate limited to ensure service stability. Limits depend on your plan.
Response Headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per hour |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
429 Too Many Requests error. Wait for the time indicated in the Retry-After header. Error Codes
The API uses standard HTTP codes to indicate request results.
| Code | Error | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request parameters |
| 401 | INVALID_API_KEY | Invalid or missing API key |
| 401 | API_KEY_REVOKED | The API key has been revoked |
| 401 | API_KEY_EXPIRED | The API key has expired |
| 403 | INSUFFICIENT_SCOPES | The API key lacks required permissions |
| 403 | CLINIC_MANAGER_ACCESS_DENIED | The user is not a manager of the requested clinic |
| 403 | IP_NOT_ALLOWED | Access denied from this IP address |
| 404 | NOT_FOUND | Resource not found |
| 404 | SALE_NOT_FOUND | The requested sale does not exist in this clinic |
| 404 | INVOICE_NOT_FOUND | The requested invoice or rectified invoice does not exist in this clinic |
| 404 | INVOICE_PDF_NOT_AVAILABLE | The invoice exists but its PDF has not been generated yet |
| 404 | TBAI_NOT_AVAILABLE | The invoice is not registered with TicketBai (Basque Country) so no XML exists |
| 400 | INVALID_LIMIT | limit is not an integer between 1 and 100 |
| 400 | INVALID_OFFSET | offset is negative; or — on the sales/payments/invoices/products list endpoints — not a multiple of limit |
| 400 | OFFSET_TOO_LARGE | offset + limit exceeds 10000 (Elasticsearch result window; applies to /clients and /clients/autocomplete) |
| 400 | INVALID_DATE_FORMAT | start_date or end_date is not ISO 8601 UTC (e.g. 2026-01-01T00:00:00Z) |
| 400 | INVALID_DATE_RANGE | start_date is after end_date or required dates are missing |
| 400 | DATE_RANGE_TOO_LARGE | The window between start_date and end_date exceeds 366 days |
| 400 | INVALID_SALE_NUMBER | saleNumber is not a positive integer |
| 400 | INVALID_SALE_STATUS | sale_status contains a value outside completed, paid_partially, unpaid |
| 400 | INVALID_TYPE | type is not one of invoice, rectified_invoice |
| 400 | INVALID_CLIENT_ID | client_id is not a valid UUID |
| 409 | CLIENT_EMAIL_EXISTS | A client with this email already exists |
| 429 | RATE_LIMIT_EXCEEDED | Request rate limit exceeded |
| 500 | INTERNAL_ERROR | Internal server error |
| 503 | API_DISABLED | The API is temporarily disabled |
Error Format
{"data": null,
"errors": [
{"code": "INVALID_API_KEY",
"message": "Invalid API key"
}
],
"meta": {"timestamp": "2026-02-05T12:00:00+00:00",
"api_version": "v1"
}
}User & Profiles
Endpoints to access user and profile information.
/api-connector/v1/meGet the authenticated user information.
Response Example
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"fullname": "John Doe",
"firstname": "John",
"lastname": "Doe",
"timezone": "Europe/Madrid",
"locale": "es_ES",
"country_code": "ES",
"status": "active"
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/profilesList all professional profiles for the authenticated user.
Response Example
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"type": "professional",
"back_office_clinic_id": "550e8400-e29b-41d4-a716-446655440002",
"last_selected": true
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Clinics
Endpoints to access back office clinic information.
/api-connector/v1/backofficeclinicsList all back office clinics associated with the authenticated user.
Response Example
{
"data": [
{
"id": "6edafcff-732d-4cce-8f16-f0d50d2b22b2",
"name": "Central Clinic",
"business_name": "Central Clinic S.L.",
"vat_number": "B12345678",
"address": "Main Street 1",
"city": "Madrid",
"zip_code": "28001",
"has_logo": true,
"subscription_status": "active"
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}Get details of a specific back office clinic.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
Response Example
{
"data": {
"id": "6edafcff-732d-4cce-8f16-f0d50d2b22b2",
"name": "Central Clinic",
"business_name": "Central Clinic S.L.",
"vat_number": "B12345678",
"issuer_type": "company",
"address": "Main Street 1",
"city": "Madrid",
"zip_code": "28001",
"has_logo": true,
"custom_email": "[email protected]",
"default_appointments_behaviour": "confirmed",
"subscription_status": "active"
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Calendars
Endpoints to access calendars (physical locations where appointments occur).
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/calendarsList all calendars for a clinic. A calendar represents a physical location.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
Response Example
{
"data": [
{
"id": "6e8e058e-f01b-454b-8d49-77589dadc7bd",
"name": "Centro Getxo",
"address": "Getxo, España",
"city": "Getxo",
"zip_code": "48993",
"type": "onsite",
"is_active": true
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Staff
Endpoints to access staff members and their calendars.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/staffList all staff members for a clinic.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
Response Example
{
"data": [
{
"id": "e727a18f-edf0-45f5-a866-d2c629113758",
"name": "Pedro",
"surname": "García",
"fullname": "Pedro García",
"email": "[email protected]",
"is_active": true
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/staffcalendarsList staff calendars for a clinic. A staff calendar links a staff member to a calendar with specific settings. Supports optional filtering by calendar or staff.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
calendar_id | string (UUID) | query | No | Filter by calendar UUID |
staff_id | string (UUID) | query | No | Filter by back office staff UUID |
service_id | string (UUID) | query | No | Return only staff calendars configured to provide this service (empty list if the service is unknown) |
limit | integer | query | No | Max results to return (1-100, default: 100) |
offset | integer | query | No | Number of results to skip (default: 0) |
order_by | string | query | No | Sort field and direction (default: "email.asc") |
Response Example
{
"data": [
{
"id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"back_office_staff_id": "e727a18f-edf0-45f5-a866-d2c629113758",
"calendar_id": "6e8e058e-f01b-454b-8d49-77589dadc7bd",
"email": "[email protected]",
"telephone": "+34600000000",
"slot_duration": 15,
"is_active": true,
"timezone": "Europe/Madrid"
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Services
Endpoints to access the services catalog.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/servicesList the clinic service catalogue (treatments). Optionally filter by staff member (staff_id) or calendar/gabinete (calendar_id), and expand the staff/calendar tuples each service is configured for via expand=staff_calendars. Amounts are returned in cents.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
staff_id | string (UUID) | query | No | Return only services offered by this staff member |
calendar_id | string (UUID) | query | No | Return only services offered in this calendar (gabinete) |
expand | string (CSV) | query | No | Comma-separated fields to expand per item. Supported: "staff_calendars" (adds a staff_calendars array of {id, staff_id, calendar_id}) |
service_category | string (UUID) | query | No | Filter by service category UUID |
order_by | string | query | No | Sort field and direction (default: "name.asc") |
limit | integer | query | No | Max results to return (1-100, default: 100) |
offset | integer | query | No | Number of results to skip (default: 0) |
Response Example
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Fisioterapia",
"service_category": "c1d2e3f4-a5b6-7890-abcd-ef1234567890",
"amount": 5000,
"duration": 30,
"price_type": 1,
"color": "#4CAF50",
"is_active": true,
"vat_rate": "21",
"note": null,
"location_type": "onsite",
"scope": "individual",
"max_participants": null,
"position": 1,
"staff_calendars": [
{ "id": "9cf404c3-951e-4895-8a14-18c68bd91ef4", "staff_id": "e727a18f-edf0-45f5-a866-d2c629113758", "calendar_id": "6e8e058e-f01b-454b-8d49-77589dadc7bd" }
]
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Clients
Endpoints to list, search, create and retrieve clients.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/clientsList all clients for a clinic with pagination, filtering and sorting.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
limit | integer | query | No | Max results to return (default: 10, max: 100) |
offset | integer | query | No | Number of results to skip (default: 0) |
order_by | string | query | No | Sort field and direction, e.g. "name.asc", "created_at.desc" (default: "name.asc") |
gender | string | query | No | Filter by gender: "male", "female" |
min_age | string | query | No | Minimum age filter (e.g., "18") |
max_age | string | query | No | Maximum age filter (e.g., "65") |
activity_from | string (YYYY-MM-DD) | query | No | Filter clients active from this date |
activity_to | string (YYYY-MM-DD) | query | No | Filter clients active until this date |
Response Example
{
"data": {
"clients": [
{
"id": "68b70480-e924-4841-8038-e2e695971ff6",
"client_number": 1234,
"firstname": "Carlos",
"lastname": "Garcia",
"fullname": "Carlos Garcia",
"email": "[email protected]",
"phone": "+34600000000",
"gender": "male",
"date_of_birth": { "dayOfBirth": "15", "monthOfBirth": "03", "yearOfBirth": "1990" },
"address": "Calle Mayor 1",
"city": "Madrid",
"province": "Madrid",
"post_code": "28001",
"country_code": "ES",
"id_card": "12345678A",
"note": "Paciente habitual",
"tags": ["vip"],
"is_active": true,
"is_company": false,
"walk_in": false,
"receive_reminders": true,
"receive_campaigns": true,
"has_photo": false,
"created_at": "2026-01-15T10:00:00+00:00"
}
],
"meta": {
"limit": 10,
"offset": 0,
"total": 156
}
},
"errors": [],
"meta": { "timestamp": "2026-03-04T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/clients/{clientId}Get details of a specific client by ID.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
clientId | string (UUID) | path | Yes | UUID of the client |
Response Example
{
"data": {
"id": "68b70480-e924-4841-8038-e2e695971ff6",
"client_number": 1234,
"firstname": "Carlos",
"lastname": "García",
"fullname": "Carlos García",
"email": "[email protected]",
"phone": "+34600000000",
"gender": "male",
"date_of_birth": { "dayOfBirth": "15", "monthOfBirth": "03", "yearOfBirth": "1990" },
"address": "Calle Mayor 1",
"city": "Madrid",
"province": "Madrid",
"post_code": "28001",
"country_code": "ES",
"id_card": "12345678A",
"note": "Paciente habitual",
"tags": ["vip"],
"is_active": true,
"is_company": false,
"walk_in": false,
"receive_reminders": true,
"receive_campaigns": true,
"has_photo": false,
"created_at": "2026-01-15T10:00:00+00:00"
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/clients/autocompleteSearch clients by name, surname, email, phone or ID card. Supports partial and accent-insensitive matching.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
keyword | string | query | Yes | Search query (name, surname, email, phone, or ID card) |
limit | integer | query | No | Max results to return (default: 20) |
offset | integer | query | No | Number of results to skip (default: 0) |
order_by | string | query | No | Sort field and direction (e.g. name.asc) |
Response Example
{
"data": [
{
"id": "68b70480-e924-4841-8038-e2e695971ff6",
"fullname": "Carlos García",
"email": "[email protected]",
"telephone": "+34600000000",
"client_number": 1234
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/clientsCreate a new client. firstname/lastname/phone are the canonical field names (they match what the GET endpoints return); name/surname/telephone are accepted as legacy aliases. If both forms are sent in the same request, the canonical value wins. The response includes both the canonical fields and the legacy aliases for backwards compatibility.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
firstname | string | body | No | Client first name (canonical). Legacy alias: "name" |
lastname | string | body | No | Client last name (canonical). Legacy alias: "surname" |
phone | string | body | No | Phone in international format (canonical). Legacy alias: "telephone" |
email | string | body | No | Client email address |
walk_in | boolean | body | No | Is walk-in client (default: false) |
Request Body
{
"firstname": "Carlos",
"lastname": "García",
"phone": "+34600000000",
"email": "[email protected]"
}Response Example
{
"data": {
"id": "68b70480-e924-4841-8038-e2e695971ff6",
"client_number": 1234,
"firstname": "Carlos",
"lastname": "García",
"fullname": "Carlos García",
"email": "[email protected]",
"phone": "+34600000000",
"name": "Carlos",
"surname": "García",
"telephone": "+34600000000"
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Availability
Endpoints to check availability and occupied time slots.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/calendars/{calendarId}/schedulesGet staff working hours (schedules) for a date range. Returns day offs and time offs as well.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
calendarId | string (UUID) | path | Yes | UUID of the calendar |
start_date | string (YYYY-MM-DD) | query | Yes | Start date |
end_date | string (YYYY-MM-DD) | query | No | End date (defaults to start_date) |
Response Example
{
"data": [
{
"staff_id": "e727a18f-edf0-45f5-a866-d2c629113758",
"staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"name": "Pedro García",
"schedules": [
{
"day": "THURSDAY",
"date": "2026-02-05",
"type": "REGULAR_DAY",
"hours": [
{ "start": "08:00", "end": "14:00", "type": "WORKING_DAY" },
{ "start": "16:00", "end": "20:00", "type": "WORKING_DAY" }
]
}
]
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/calendars/{calendarId}/occupiedGet all occupied time slots: appointments, time reservations, group reservations, day offs, and working hours.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
calendarId | string (UUID) | path | Yes | UUID of the calendar |
start_date | string (YYYY-MM-DD) | query | Yes | Start date |
end_date | string (YYYY-MM-DD) | query | Yes | End date |
staff_calendar_id | string (UUID) | query | No | Filter by staff calendar |
Response Example
{
"data": [
{
"staff_id": "e727a18f-edf0-45f5-a866-d2c629113758",
"staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"name": "Pedro García",
"working_hours": [
{ "date": "2026-02-05", "from": "08:00", "to": "20:00", "type": "WORKING_DAY" }
],
"day_offs": [],
"time_offs": [],
"appointments": [
{
"id": "31bd18a6-52be-4ad3-9b9f-78b684fc6f2a",
"from": "2026-02-05T17:00:00Z",
"to": "2026-02-05T17:30:00Z",
"behaviour": "confirmed",
"client_id": "68b70480-e924-4841-8038-e2e695971ff6",
"service_name": "Fisioterapia"
}
],
"time_reservations": [
{
"id": "ac14f29b-89f0-4d16-873c-772dae27650f",
"from": "2026-02-05T13:00:00Z",
"to": "2026-02-05T14:00:00Z",
"reason": "Lunch break"
}
],
"group_reservations": []
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/calendars/{calendarId}/check-availabilityCheck if a specific time slot is available. Returns conflicts if not available.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
calendarId | string (UUID) | path | Yes | UUID of the calendar |
from | string (ISO 8601) | query | Yes | Start datetime in UTC (e.g., 2026-02-05T10:00:00Z) |
to | string (ISO 8601) | query | Yes | End datetime in UTC |
staff_calendar_id | string (UUID) | query | Yes | UUID of the staff calendar |
Response Example
{
"data": {
"available": false,
"requested": {
"from": "2026-02-05T13:00:00Z",
"to": "2026-02-05T14:00:00Z",
"staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4"
},
"conflicts": [
{
"type": "time_reservation",
"id": "ac14f29b-89f0-4d16-873c-772dae27650f",
"from": "2026-02-05T13:00:00Z",
"to": "2026-02-05T14:00:00Z",
"reason": "Lunch break"
}
]
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/calendars/{calendarId}/available-slotsGet all time slots with availability status. Useful for rendering a booking calendar.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
calendarId | string (UUID) | path | Yes | UUID of the calendar |
start_date | string (YYYY-MM-DD) | query | Yes | Start date |
end_date | string (YYYY-MM-DD) | query | Yes | End date |
staff_calendar_id | string (UUID) | query | No | Filter by staff calendar |
service_id | string (UUID) | query | No | Filter slots that fit the given service duration |
slot_duration | integer | query | No | Slot duration in minutes (default: 15) |
service_duration | integer | query | No | Service duration to check fit (default: 0) |
period | string (morning|afternoon) | query | No | Keep only morning (before 14:00) or afternoon (14:00 onwards) slots, in the staff member timezone |
weekdays | string (CSV 1-7) | query | No | Comma-separated ISO weekdays to keep (1=Monday ... 7=Sunday) |
date | string (YYYY-MM-DD) | query | No | Return only this single day. Must fall within start_date/end_date |
Response Example
{
"data": [
{
"staff_id": "e727a18f-edf0-45f5-a866-d2c629113758",
"staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"name": "Pedro García",
"timezone": "Europe/Madrid",
"slot_duration": 15,
"available_slots": [
{
"date": "2026-02-05",
"day": "THURSDAY",
"type": "REGULAR_DAY",
"slots": [
{ "from": "2026-02-05T08:00:00Z", "to": "2026-02-05T08:15:00Z", "available": true, "hour_type": "WORKING_DAY" },
{ "from": "2026-02-05T13:00:00Z", "to": "2026-02-05T13:15:00Z", "available": false, "hour_type": "WORKING_DAY" }
]
}
]
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/available-slotsConvenience endpoint for conversational agents: resolves the calendar (gabinete) from calendar_name and returns the same availability payload as the by-id endpoint, including the optional period/weekdays/date filters. Name matching is Unicode-aware and case-insensitive (Á ↔ á, Í ↔ í). By default accents matter; pass accent_insensitive=true to also ignore diacritics (Podologia ↔ Podología). Returns 404 (CALENDAR_NOT_FOUND) when no calendar matches and 409 (CALENDAR_NAME_AMBIGUOUS) when more than one calendar shares the name — candidates are listed in meta.candidates.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
calendar_name | string | query | Yes | Calendar (gabinete) name. Unicode-aware case-insensitive exact match; accents matter unless accent_insensitive=true |
accent_insensitive | boolean | query | No | When true, diacritics are ignored when matching calendar_name (Podologia ↔ Podología). Default: false |
start_date | string (YYYY-MM-DD) | query | Yes | Start date (strict YYYY-MM-DD format) |
end_date | string (YYYY-MM-DD) | query | Yes | End date (strict YYYY-MM-DD format). Range cannot exceed 15 days |
period | string (morning|afternoon) | query | No | Keep only morning (before 14:00) or afternoon (14:00 onwards) slots, in the staff member timezone |
weekdays | string (CSV 1-7) | query | No | Comma-separated ISO weekdays to keep (1=Monday ... 7=Sunday) |
date | string (YYYY-MM-DD) | query | No | Return only this single day. Must fall within start_date/end_date |
staff_calendar_id | string (UUID) | query | No | Filter by a single staff calendar UUID |
service_id | string (UUID) | query | No | Filter slots that fit the given service duration |
Response Example
{
"data": [
{
"staff_id": "e727a18f-edf0-45f5-a866-d2c629113758",
"staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"name": "Pedro García",
"timezone": "Europe/Madrid",
"slot_duration": 15,
"available_slots": [
{
"date": "2026-02-05",
"day": "THURSDAY",
"type": "REGULAR_DAY",
"slots": [
{ "from": "2026-02-05T08:00:00Z", "to": "2026-02-05T08:15:00Z", "available": true, "hour_type": "WORKING_DAY" }
]
}
]
}
],
"errors": [],
"meta": {
"timestamp": "2026-02-05T12:00:00+00:00",
"api_version": "v1",
"calendar": { "id": "5c3f...", "name": "Gabinete 1" }
}
}Appointments
Endpoints to create and manage appointments.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/availabilityGet appointments, time reservations, group reservations, and recurring events for a date range. Maximum range: 7 days.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
start_date | string (ISO 8601) | query | Yes | Start datetime in UTC (e.g., 2026-03-05T23:00:00Z) |
end_date | string (ISO 8601) | query | Yes | End datetime in UTC (e.g., 2026-03-06T22:59:59Z) |
local_start_date | string (YYYY-MM-DD) | query | No | Local start date for recurring events (e.g., 2026-03-06). Required to get recurring_events — see note below. |
local_end_date | string (YYYY-MM-DD) | query | No | Local end date for recurring events (e.g., 2026-03-06). Required to get recurring_events — see note below. |
result_type | string | query | No | Filter by type: "appointments", "time_reservations", "group_reservations", "recurring_events". Returns all if omitted. |
client_id | string (UUID) | query | No | Filter appointments by client |
staff_id | string (UUID) | query | No | Filter by staff |
calendar_id | string (UUID) | query | No | Filter by calendar |
sale_status | string | query | No | Filter by sale status |
limit | integer | query | No | Max results to return (default: all) |
offset | integer | query | No | Number of results to skip (default: 0) |
Response Example
{
"data": {
"appointments": [
{
"id": "31bd18a6-52be-4ad3-9b9f-78b684fc6f2a",
"client_id": "68b70480-e924-4841-8038-e2e695971ff6",
"client_name": "Carlos Garcia",
"service_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"service_name": "Consulta general",
"calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"staff_id": "e727a18f-edf0-45f5-a866-d2c629113758",
"booked_from": "2026-03-06T09:00:00Z",
"booked_to": "2026-03-06T09:30:00Z",
"status": "confirmed",
"sale_status": "pending",
"note": "Primera visita",
"price": 5000,
"source": "backoffice"
}
],
"time_reservations": [
{
"id": "ac14f29b-89f0-4d16-873c-772dae27650f",
"back_office_staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"name": "Lunch break",
"booked_from": "2026-03-06T12:00:00Z",
"booked_to": "2026-03-06T13:00:00Z",
"note": null
}
],
"group_reservations": [],
"recurring_events": [
{
"id": "b5c6d7e8-f9a0-1234-5678-9abcdef01234",
"back_office_staff_calendar_id": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"name": "Weekly team meeting",
"booked_from": "2026-03-06T08:00:00",
"booked_to": "2026-03-06T08:30:00",
"is_recurring_event": true,
"original_id": "a1b2c3d4-0000-0000-0000-000000000001",
"timezone": "Europe/Madrid"
}
]
},
"errors": [],
"meta": { "timestamp": "2026-03-06T12:00:00+00:00", "api_version": "v1" }
}
IMPORTANT — Recurring Events (recurring_events):
Recurring events are generated from EventSchedules and calculated based on the clinic's local timezone, not UTC. To receive them, you MUST provide both local_start_date and local_end_date in YYYY-MM-DD format (e.g., 2026-03-06). If these parameters are omitted, the recurring_events array will be empty. The start_date/end_date parameters (UTC, ISO 8601) are used for appointments, time_reservations, and group_reservations only./api-connector/v1/backofficeclinics/{backOfficeClinicId}/appointmentsCreate one or more appointments.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
client | string (UUID) | body | No | Client UUID (omit for walk-in) |
timezone | string | body | No | Timezone (default: Europe/Madrid) |
appointments | array | body | Yes | Array of appointment objects |
appointments[].service | string (UUID) | body | Yes | Service UUID |
appointments[].back_office_staff_calendar | string (UUID) | body | Yes | Staff calendar UUID |
appointments[].from | string (ISO 8601) | body | Yes | Start datetime in UTC |
appointments[].to | string (ISO 8601) | body | Yes | End datetime in UTC |
appointments[].note | string | body | No | Note for the appointment |
send_reminder | boolean | body | No | Send reminder to client (default: true) |
behaviour | string | body | No | Status: "confirmed", "pending", "cancelled" (default: confirmed) |
force | boolean | body | No | Allow overlapping appointments (default: false) |
Request Body
{
"client": "68b70480-e924-4841-8038-e2e695971ff6",
"timezone": "Europe/Madrid",
"appointments": [
{
"service": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"back_office_staff_calendar": "9cf404c3-951e-4895-8a14-18c68bd91ef4",
"from": "2026-02-05T15:00:00Z",
"to": "2026-02-05T15:30:00Z",
"note": "First consultation"
}
],
"send_reminder": true,
"behaviour": "confirmed"
}Response Example
{
"data": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"appointment_number": 12346,
"client_id": "68b70480-e924-4841-8038-e2e695971ff6",
"service_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"service_name": "Fisioterapia",
"booked_from": "2026-02-05T15:00:00Z",
"booked_to": "2026-02-05T15:30:00Z",
"status": "confirmed",
"note": "First consultation"
}
],
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/appointments/{appointmentId}/behaviourChange the behaviour (status) of an appointment. Use this to confirm, cancel, mark as no-show, or revert to unconfirmed.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
appointmentId | string (UUID) | path | Yes | UUID of the appointment |
behaviour | string | body | Yes | New status: "unconfirmed", "confirmed", "cancelled" or "no_show" |
force_capacity_increase | boolean | body | No | Force capacity increase when reactivating a cancelled participant in a full group appointment (default: false) |
cancel_participants | boolean | body | No | When cancelling a group appointment, also cancel all participants (default: false) |
Request Body
{
"behaviour": "cancelled"
}Response Example
{
"data": {
"appointment_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"behaviour": "cancelled",
"message": "Appointment behaviour updated successfully"
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/appointments/{appointmentId}Delete an appointment permanently. The appointment id is obtained from the availability or create endpoints.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
appointmentId | string (UUID) | path | Yes | UUID of the appointment to delete |
Response Example
{
"data": {
"message": "Appointment deleted successfully"
},
"errors": [],
"meta": { "timestamp": "2026-02-05T12:00:00+00:00", "api_version": "v1" }
}Sales
Endpoints to retrieve sales information for a clinic. The light list view (paginated, header-level fields only) is paired with a per-sale detail endpoint for drill-downs.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/salesList sales for a clinic (light view, paginated, ordered by saleNumber DESC). Each row carries only the aggregate-level fields (id, saleNumber, status, saleStatus, createdAt, paidAt, invoiceId, pit). Products and payments are NOT included — drill down with GET /sales/{saleNumber} for the full detail. The date window applies to the sale's created_at, is required, and capped at 366 days.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
start_date | string (ISO 8601 UTC, e.g. 2026-01-01T00:00:00Z) | query | Yes | Start of date window. Applied on the sale created_at. |
end_date | string (ISO 8601 UTC) | query | Yes | End of date window. Max range: 366 days. |
limit | integer | query | No | Items per page (default 50, 1-100) |
offset | integer | query | No | Items to skip (default 0, must be a multiple of limit) |
sale_status | string (CSV) | query | No | Filter by sale status: completed, paid_partially, unpaid |
Response Example
{
"data": {
"sales": [
{
"id": "sale-uuid",
"saleNumber": 1234,
"status": "active",
"saleStatus": "paid_partially",
"createdAt": "2026-05-20T11:30:00Z",
"paidAt": null,
"invoiceId": "550e8400-e29b-41d4-a716-446655440099",
"pit": "0.00"
}
],
"meta": { "limit": 50, "offset": 0, "total": 1 }
},
"errors": [],
"meta": { "timestamp": "2026-05-20T12:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/sales/{saleNumber}Get the full sale aggregate by its per-clinic sale number: payments[], appointments[], clientVouchers[], clientItems[], clientBudgets[], clientSubscriptions[], invoiceId, saleStatus, pit, receipt. Use the saleNumber returned by /payments or /sales to drill down.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
saleNumber | integer | path | Yes | Sequential sale number within the clinic |
Response Example
{
"data": {
"id": "sale-uuid",
"saleNumber": 1234,
"payments": [],
"pit": null,
"invoiceIssuer": null,
"discountValue": null,
"discountType": "amount",
"discountScope": "sale",
"discountedProducts": [],
"appointments": [],
"clientVouchers": [],
"clientItems": [],
"clientBudgets": [],
"clientSubscriptions": [],
"invoiceId": null,
"receipt": null
},
"errors": [],
"meta": { "timestamp": "2026-05-20T12:00:00+00:00", "api_version": "v1" }
}Products
Granular sold-line-items endpoint: equivalent to the dashboard ARTÍCULOS tab. Each row is one transactional line within a Sale (appointment, voucher, item, subscription or budget). This is NOT the service catalog — for that use /services.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/productsList sold products for a clinic, one row per line item, ordered by product/appointment date DESC. Monetary values (subTotal, tax, irpf, price, amountPaid, commission) come in the clinic's local currency (not cents). amountPaid is the portion of price already collected — compare against price to spot partial payments. The client's DNI is exposed under client.idCard. Use by_payments=true to swap the date window semantics from product/appointment date to payment registration date (Spanish 'fecha de cobro') — handy for cash-flow reconciliation by charge date. Drill-downs: sale.sequenceNumber → GET /sales/{saleNumber}; invoice.id (when non-null) → GET /invoices/{id}. Date window is required and capped at 366 days.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
start_date | string (ISO 8601 UTC) | query | Yes | Start of date window (applied on product/appointment date by default, or on payment createdAt if by_payments=true) |
end_date | string (ISO 8601 UTC) | query | Yes | End of date window. Max range: 366 days. |
limit | integer | query | No | Items per page (default 50, 1-100) |
offset | integer | query | No | Items to skip (default 0, must be a multiple of limit) |
type_of_product | string (CSV) | query | No | Product types: appointment, voucher, item, subscription, budget |
staff_id | string (CSV of UUIDs) | query | No | Filter by staff member(s) |
calendar_id | string (CSV of UUIDs) | query | No | Filter by calendar(s) |
client_id | string (UUID) | query | No | Filter by client |
services | string (CSV of UUIDs) | query | No | Filter by service(s) |
payment_status | string (CSV) | query | No | completed, paid_partially, unpaid, free |
payment_method | string (CSV) | query | No | efectivo, transferencia, tarjeta, bizum, paypal, otro, … |
by_payments | boolean | query | No | If true, the date window applies to payment.createdAt instead of product/appointment date |
Response Example
{
"data": {
"products": [
{
"id": "product-uuid",
"type": "appointment",
"name": "Consulta inicial",
"sequenceNumber": 4521,
"subTotal": 50.00,
"tax": 10.50,
"irpf": 0.00,
"price": 60.50,
"amountPaid": 60.50,
"commission": 0.00,
"sale": {
"id": "sale-uuid",
"sequenceNumber": 1234,
"paymentStatus": "completed",
"paymentMethod": "tarjeta",
"paidAt": "2026-05-20T11:30:00Z"
},
"client": {
"id": "client-uuid",
"name": "María García",
"phone": "+34600000000",
"clientNumber": 56,
"idCard": "12345678A"
},
"employee": { "id": "staff-uuid", "name": "Dra. López" },
"calendar": { "id": "cal-uuid", "name": "Consulta 1" },
"invoice": { "id": "invoice-uuid", "sequenceNumber": 99 }
}
],
"meta": { "limit": 50, "offset": 0, "total": 1 }
},
"errors": [],
"meta": { "timestamp": "2026-05-20T12:00:00+00:00", "api_version": "v1" }
}Payments
Cash-flow movements (cobros y reembolsos) for a clinic. Each row is one monetary transaction. Use transactionType to distinguish PAYMENT (positive amount) from REFUND (negative amount). Sum amount directly to get period nets — refunds self-cancel against their original charge.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/paymentsList payment transactions for a clinic, ordered by paidAt DESC. amountRefunded semantics: on PAYMENT rows, the cumulative amount already returned FROM this specific charge. On REFUND rows, always 0 (refunds are not refundable). Do NOT use amountRefunded to compute period nets — sum amount instead. Drill-down: every row carries saleNumber (per-clinic sequential ID). Use it with GET /sales/{saleNumber} to fetch the full sale (products, invoice, aggregated payments). The optional client_id filter walks payments → sale_payments → sales → products (JOINED inheritance) and matches the client_id stored on each Product subclass. Date window is required and capped at 366 days.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
start_date | string (ISO 8601 UTC) | query | Yes | Start of date window (applied on payment paidAt) |
end_date | string (ISO 8601 UTC) | query | Yes | End of date window. Max range: 366 days. |
limit | integer | query | No | Items per page (default 50, 1-100) |
offset | integer | query | No | Items to skip (default 0, must be a multiple of limit) |
client_id | string (UUID) | query | No | Returns only payments whose sale has a product linked to this client (appointment, voucher, item, budget or subscription). |
Response Example
{
"data": {
"payments": [
{
"id": "payment-uuid",
"sequence": 4321,
"amount": 60.50,
"paymentMethod": "tarjeta",
"saleNumber": 1234,
"paidAt": "2026-05-20T11:35:00Z",
"user": "[email protected]",
"amountRefunded": 0,
"transactionType": "PAYMENT",
"customPaymentMethodId": null
},
{
"id": "refund-uuid",
"sequence": 4322,
"amount": -5.00,
"paymentMethod": "tarjeta",
"saleNumber": 1234,
"paidAt": "2026-05-21T09:00:00Z",
"user": "[email protected]",
"amountRefunded": 0,
"transactionType": "REFUND",
"customPaymentMethodId": null
}
],
"meta": { "limit": 50, "offset": 0, "total": 2 }
},
"errors": [],
"meta": { "timestamp": "2026-05-20T12:00:00+00:00", "api_version": "v1" }
}Invoices
Fiscal documents for a clinic: PermanentInvoice (type=invoice) and RectifiedInvoice (type=rectified_invoice, the Spanish factura rectificativa, equivalent to a credit note when amounts are negative). Both families are merged at SQL level and paginated together for a chronologically coherent stream. Invoice drafts (mutable, no legal value) are excluded by design.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoicesList invoices and rectified invoices (unified, light view), paginated, ordered by issue date DESC. Each row carries a type discriminator (invoice / rectified_invoice). On rectified_invoice rows, references_invoice_id points to the original invoice. Status semantics: created (live), rectified (a rectified_invoice references it), voided. links.pdf gives a relative URL to download the invoice PDF. For the optional TicketBai XML link, use the detail endpoint. For full detail (lines, totals, addresses), use GET /invoices/{invoiceId}. The date window applies to the document's issue date and is capped at 366 days.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
start_date | string (ISO 8601 UTC) | query | Yes | Start of date window (applied on invoice date) |
end_date | string (ISO 8601 UTC) | query | Yes | End of date window. Max range: 366 days. |
limit | integer | query | No | Items per page (default 50, 1-100) |
offset | integer | query | No | Items to skip (default 0, must be a multiple of limit) |
type | string | query | No | Filter by document family: invoice, rectified_invoice |
client_id | string (UUID) | query | No | Filter by a single client |
Response Example
{
"data": {
"invoices": [
{
"id": "550e8400-e29b-41d4-a716-446655440099",
"type": "invoice",
"invoice_number": "A-0001",
"date": "2026-05-20",
"status": "created",
"client": {
"id": "client-uuid",
"name": "María García",
"id_card": "12345678A",
"number": 56
},
"business_name": "María García",
"references_invoice_id": null,
"rectification": null,
"links": {
"pdf": "/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoices/550e8400-e29b-41d4-a716-446655440099/pdf"
},
"created_at": "2026-05-20T11:40:00Z"
},
{
"id": "660e8400-e29b-41d4-a716-446655440111",
"type": "rectified_invoice",
"invoice_number": "R-0001",
"date": "2026-05-21",
"status": "created",
"client": {
"id": "client-uuid",
"name": "María García",
"id_card": "12345678A",
"number": 56
},
"business_name": "María García",
"references_invoice_id": "550e8400-e29b-41d4-a716-446655440099",
"rectification": { "type": "ordinary" },
"links": {
"pdf": "/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoices/660e8400-e29b-41d4-a716-446655440111/pdf"
},
"created_at": "2026-05-21T09:05:00Z"
}
],
"meta": { "limit": 50, "offset": 0, "total": 2 }
},
"errors": [],
"meta": { "timestamp": "2026-05-21T10:00:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoices/{invoiceId}Show a single invoice or rectified invoice by UUID. The endpoint resolves the UUID transparently in both tables — the client doesn't need to know upfront which family the document belongs to. Monetary semantics: type=invoice carries positive amounts; type=rectified_invoice carries NEGATIVE amounts by convention. Sum invoices + rectified_invoices to get net revenue. links: pdf (always present) and xml_tbai (only on TicketBai-enabled invoices, otherwise null).
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
invoiceId | string (UUID) | path | Yes | UUID of the invoice or rectified invoice |
Response Example
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440099",
"type": "invoice",
"invoice_number": "A-0001",
"date": "2026-05-20",
"status": "created",
"client": {
"id": "client-uuid",
"name": "María García",
"id_card": "12345678A",
"number": 56
},
"business_name": "María García",
"vat_number": "B12345678",
"address": "Calle Mayor 1, 28013 Madrid",
"lines": [
{ "name": "Consulta inicial", "quantity": 1, "subtotal": 50.00, "tax": 10.50, "total": 60.50 }
],
"totals": { "subtotal": 50.00, "taxes": 10.50, "total": 60.50 },
"references_invoice_id": null,
"rectification": null,
"links": {
"pdf": "/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoices/550e8400-e29b-41d4-a716-446655440099/pdf",
"xml_tbai": null
}
},
"errors": [],
"meta": { "timestamp": "2026-05-21T10:05:00+00:00", "api_version": "v1" }
}/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoices/{invoiceId}/pdfDownload the invoice PDF as a binary stream (Content-Type: application/pdf). Works for both invoice and rectified_invoice. Returns 404 INVOICE_PDF_NOT_AVAILABLE if the PDF has not been generated yet for this document.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
invoiceId | string (UUID) | path | Yes | UUID of the invoice or rectified invoice |
Response Example
(binary PDF body — Content-Type: application/pdf)/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoices/{invoiceId}/xml-tbaiDownload the TicketBai XML for the invoice (Content-Type: application/xml). Only available on invoices registered with TicketBai (Basque Country territory). Returns 404 TBAI_NOT_AVAILABLE for non-TBAI invoices.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
invoiceId | string (UUID) | path | Yes | UUID of the invoice or rectified invoice |
Response Example
(TBAI XML body — Content-Type: application/xml)Invoice Issuers
Invoice issuers (emisores) configured for a clinic — the fiscal entities (autónomos, sociedades) under which invoices are issued. Each clinic can have multiple issuers; each invoice carries the issuer used at the time of emission.
/api-connector/v1/backofficeclinics/{backOfficeClinicId}/invoice-issuersList all invoice issuers configured for the clinic, with their fiscal data (id, name, vat_number, address, business_name, …). Use the returned id to filter or interpret invoice responses.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
backOfficeClinicId | string (UUID) | path | Yes | UUID of the back office clinic |
Response Example
{
"data": {
"invoice_issuers": [
{
"id": "issuer-uuid",
"name": "María García Autónoma",
"business_name": "María García",
"vat_number": "12345678A",
"address": "Calle Mayor 1, 28013 Madrid"
}
]
},
"errors": [],
"meta": { "timestamp": "2026-05-21T10:10:00+00:00", "api_version": "v1" }
}