MENU navbar-image

Introduction

API documentation for the VetLlama white-label SaaS backend.

VetLlama is a white-label API backend for tenant-owned service booking businesses. The current API foundation covers platform administration, public tenant owner signup, tenant owner authentication, tenant/domain resolution, public tenant configuration, and limited end-user magic-link authentication.

All requests should send `Accept: application/json`. Authenticated routes use JWT Bearer tokens in the `Authorization` header.

Tenant-scoped public and end-user flows resolve tenant context from the request host. In local development, use a host such as `demo.vetllama.test` when calling `/api/public/*` and `/api/user/*` tenant-scoped endpoints.

The documentation is organized around three actor surfaces: Admin, Tenant, and User.

Development seed credentials, when shown, are for local environments only and must not be used in production.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Use the JWT returned by the relevant login flow: Admin, TenantOwner, or EndUser magic-link verification. Tokens are scoped by guard; an Admin token cannot access TenantOwner or EndUser routes.

Admin

Auth

Login

Authenticates a platform admin and returns an Admin-scoped JWT. Development seed example only: admin@vetllama.test / LocalDevPassword123!.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/admin/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"admin@vetllama.test\",
    \"password\": \"LocalDevPassword123!\",
    \"device_token\": \"web-admin-device\"
}"
const url = new URL(
    "https://api.vetllama.com/api/admin/auth/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "admin@vetllama.test",
    "password": "LocalDevPassword123!",
    "device_token": "web-admin-device"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Logged in successfully.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "admin",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "name": "VetLlama Dev Admin",
            "email": "admin@vetllama.test",
            "is_active": true,
            "email_verified_at": "2026-05-17T00:00:00.000000Z"
        }
    }
}
 

Example response (422):


{
    "success": false,
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "Invalid email or password."
        ]
    }
}
 

Request      

POST api/admin/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Admin email address. Example: admin@vetllama.test

password   string     

Admin password. Example: LocalDevPassword123!

device_token   string  optional    

Optional device token for future push/session tracking. Example: web-admin-device

Logout

requires authentication

Invalidates the current Admin JWT and closes the latest open auth activity session.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/admin/auth/logout" \
    --header "Authorization: Bearer {ADMIN_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/admin/auth/logout"
);

const headers = {
    "Authorization": "Bearer {ADMIN_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Logged out successfully.",
    "data": null
}
 

Example response (401):


{
    "success": false,
    "message": "Unauthenticated."
}
 

Request      

POST api/admin/auth/logout

Headers

Authorization        

Example: Bearer {ADMIN_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Refresh Token

requires authentication

Exchanges a valid Admin JWT for a fresh Admin JWT.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/admin/auth/refresh" \
    --header "Authorization: Bearer {ADMIN_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/admin/auth/refresh"
);

const headers = {
    "Authorization": "Bearer {ADMIN_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Token refreshed successfully.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "admin",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "name": "VetLlama Dev Admin",
            "email": "admin@vetllama.test",
            "is_active": true
        }
    }
}
 

Request      

POST api/admin/auth/refresh

Headers

Authorization        

Example: Bearer {ADMIN_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Current Profile

requires authentication

Returns the profile for the authenticated platform admin.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/admin/auth/me" \
    --header "Authorization: Bearer {ADMIN_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/admin/auth/me"
);

const headers = {
    "Authorization": "Bearer {ADMIN_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Profile fetched successfully.",
    "data": {
        "id": 1,
        "name": "VetLlama Dev Admin",
        "email": "admin@vetllama.test",
        "is_active": true,
        "email_verified_at": "2026-05-17T00:00:00.000000Z"
    }
}
 

Request      

GET api/admin/auth/me

Headers

Authorization        

Example: Bearer {ADMIN_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Platform / System

Health Check

Confirms that the VetLlama API process is reachable.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/health" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/health"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "VetLlama API is available."
}
 

Request      

GET api/health

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Tenant

Public Auth

Register Tenant Owner

Creates a draft tenant together with its primary tenant owner, initializes onboarding defaults, creates a primary subdomain host mapping, and returns a JWT so onboarding can start immediately. If desired_subdomain is omitted, the backend generates a unique subdomain from display_name.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/public/tenant/auth/register" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"owner_name\": \"Dr. Sarah Khan\",
    \"display_name\": \"Paws & Care\",
    \"business_name\": \"Paws & Care LLC\",
    \"email\": \"owner@demo.vetllama.test\",
    \"password\": \"LocalDevPassword123!\",
    \"accepted_terms\": true,
    \"phone\": \"+10000000000\",
    \"desired_subdomain\": \"paws-care\",
    \"password_confirmation\": \"LocalDevPassword123!\"
}"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/auth/register"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "owner_name": "Dr. Sarah Khan",
    "display_name": "Paws & Care",
    "business_name": "Paws & Care LLC",
    "email": "owner@demo.vetllama.test",
    "password": "LocalDevPassword123!",
    "accepted_terms": true,
    "phone": "+10000000000",
    "desired_subdomain": "paws-care",
    "password_confirmation": "LocalDevPassword123!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "success": true,
    "message": "Account created successfully. Onboarding started.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "tenant_owner",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "tenant_id": 1,
            "name": "Dr. Sarah Khan",
            "email": "owner@paws-care.test",
            "phone": "+10000000000",
            "is_primary": true,
            "is_active": true,
            "tenant": {
                "id": 1,
                "name": "Paws & Care",
                "slug": "paws-care",
                "status": "draft",
                "onboarding_status": "in_progress",
                "is_active": true
            }
        }
    }
}
 

Example response (422):


{
    "success": false,
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email has already been taken."
        ]
    }
}
 

Request      

POST api/public/tenant/auth/register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

owner_name   string     

Primary owner name. Example: Dr. Sarah Khan

display_name   string     

Public brand or business display name. Example: Paws & Care

business_name   string  optional    

Optional legal business name. Example: Paws & Care LLC

email   string     

Unique owner email address. Example: owner@demo.vetllama.test

password   string     

Account password. Example: LocalDevPassword123!

accepted_terms   boolean     

Must be true to accept the platform terms. Example: true

phone   string  optional    

Optional owner or clinic phone number. Example: +10000000000

desired_subdomain   string  optional    

Optional subdomain label. If omitted, one is generated from display_name. Example: paws-care

password_confirmation   string     

Password confirmation. Example: LocalDevPassword123!

Public Resolution

Resolve Tenant By Host

Resolves the active tenant from a mapped website host and returns the public configuration needed by the Angular frontend. By default, resolution uses the request Host header. For testing or frontend bootstrapping, you may also pass one explicit query input: host, domain, or subdomain. Custom domains must be active and verified before they resolve publicly. Managed subdomains resolve immediately once active.

Subdomain examples resolve against VETLLAMA_TENANT_BASE_DOMAIN, such as paws-care.vetllama.test locally. Custom domain examples use the full mapped host, such as paws-care.com.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/resolve?host=paws-care.vetllama.test&domain=paws-care.com&subdomain=paws-care" \
    --header "Host: demo.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/resolve"
);

const params = {
    "host": "paws-care.vetllama.test",
    "domain": "paws-care.com",
    "subdomain": "paws-care",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Host": "demo.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Tenant configuration resolved.",
    "data": {
        "tenant": {
            "id": 1,
            "name": "Demo Clinic",
            "slug": "demo-clinic",
            "status": "active",
            "is_active": true
        },
        "template": {
            "id": 1,
            "name": "Classic Practice",
            "slug": "classic-practice",
            "description": "Local development template for Classic Practice.",
            "preview_url": "https://example.com/templates/classic-practice",
            "is_active": true,
            "schema": {
                "sections": [
                    "hero",
                    "about",
                    "banners",
                    "services",
                    "faq",
                    "contact"
                ],
                "required_fields": [
                    "homepage_content.hero_title",
                    "homepage_content.hero_subtitle",
                    "contact_details.email"
                ]
            }
        },
        "branding": {
            "primary_color": "#2563eb",
            "secondary_color": "#14b8a6",
            "logo_url": "https://example.com/demo/logo.png",
            "favicon_url": "https://example.com/demo/favicon.ico",
            "social_links": {
                "facebook": "https://facebook.com/demo",
                "instagram": "https://instagram.com/demo"
            }
        },
        "public_config": {
            "contact_details": {
                "email": "hello@demo.vetllama.test",
                "phone": "+10000000000",
                "address": "123 Demo Street"
            },
            "homepage_content": {
                "hero_title": "Care, scheduling, and client access in one place",
                "hero_subtitle": "A local-development tenant used to test public config."
            },
            "banners": [],
            "faq": [],
            "services": [],
            "settings": {
                "booking_enabled": false,
                "telehealth_enabled": false
            },
            "is_published": true
        }
    }
}
 

Example response (404):


{
    "success": false,
    "message": "Tenant could not be resolved for this host."
}
 

Request      

GET api/public/tenant/resolve

Headers

Host        

Example: demo.vetllama.test

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

host   string  optional    

Optional full host to resolve. Example: paws-care.vetllama.test

domain   string  optional    

Optional custom domain/full domain to resolve. Example: paws-care.com

subdomain   string  optional    

Optional subdomain label to resolve against the configured tenant base domain. Example: paws-care

List Public Services

Returns all enabled tenant service offerings with their active duration and pricing options. Tenant is resolved from the Host header or explicit resolver query parameters.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/services?host=pawscare.vetllama.test&domain=pawscare.test&subdomain=pawscare" \
    --header "Host: pawscare.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/services"
);

const params = {
    "host": "pawscare.vetllama.test",
    "domain": "pawscare.test",
    "subdomain": "pawscare",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Host": "pawscare.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Services fetched successfully.",
    "data": [
        {
            "id": 1,
            "type": "telehealth",
            "name": "Telehealth Follow-up",
            "description": "Video consultation for follow-ups.",
            "delivery_mode": "video",
            "duration_options": [
                {
                    "id": 1,
                    "service_offering_id": 1,
                    "duration_minutes": 30,
                    "price": "69.00",
                    "currency": "USD",
                    "is_default": true,
                    "is_active": true
                }
            ]
        }
    ]
}
 

Request      

GET api/public/tenant/services

Headers

Host        

Example: pawscare.vetllama.test

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

host   string  optional    

Optional full host to resolve. Example: pawscare.vetllama.test

domain   string  optional    

Optional custom domain/full domain to resolve. Example: pawscare.test

subdomain   string  optional    

Optional subdomain label to resolve. Example: pawscare

Get Public Service Detail

Returns one enabled public service with active duration and pricing options.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/services/16" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/services/16"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/public/tenant/services/{service_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

service_id   integer     

The ID of the service. Example: 16

List Public Locations

Returns active public locations for physical visit preparation.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/locations" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/locations"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/public/tenant/locations

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Public Policies

Returns public booking policy, FAQ, terms, privacy, and contact blocks.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/policies" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/policies"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/public/tenant/policies

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Preview Bookable Slots

Generates available bookable slot previews for an active published tenant service. This does not create or reserve a booking.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/slots?service_id=1&duration_id=1&date=2026-05-21&location_id=1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/slots"
);

const params = {
    "service_id": "1",
    "duration_id": "1",
    "date": "2026-05-21",
    "location_id": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/public/tenant/slots

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

service_id   integer     

Active service ID. Example: 1

duration_id   integer     

Active duration ID. Example: 1

date   string     

Date in YYYY-MM-DD format. Example: 2026-05-21

location_id   integer  optional    

Optional active location ID for physical visits. Example: 1

Get Public Tenant Details

Returns the full public tenant website/bootstrap payload, including tenant profile, template, branding, public content, active host mappings, active locations, enabled services, and active duration/pricing options.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/public/tenant/details?host=pawscare.vetllama.test&domain=pawscare.test&subdomain=pawscare" \
    --header "Host: pawscare.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/public/tenant/details"
);

const params = {
    "host": "pawscare.vetllama.test",
    "domain": "pawscare.test",
    "subdomain": "pawscare",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Host": "pawscare.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Tenant details fetched successfully.",
    "data": {
        "tenant": {
            "id": 1,
            "name": "Paws & Care Veterinary Clinic",
            "slug": "paws-care-vet",
            "status": "active",
            "is_active": true
        },
        "template": {},
        "branding": {},
        "public_config": {},
        "domains": [],
        "locations": [],
        "services": []
    }
}
 

Request      

GET api/public/tenant/details

Headers

Host        

Example: pawscare.vetllama.test

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

host   string  optional    

Optional full host to resolve. Example: pawscare.vetllama.test

domain   string  optional    

Optional custom domain/full domain to resolve. Example: pawscare.test

subdomain   string  optional    

Optional subdomain label to resolve. Example: pawscare

Auth

Login

Authenticates a tenant owner/operator and returns a TenantOwner-scoped JWT. Development seed example only: owner@demo.vetllama.test / LocalDevPassword123!.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"owner@demo.vetllama.test\",
    \"password\": \"LocalDevPassword123!\",
    \"device_token\": \"web-owner-device\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/auth/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "owner@demo.vetllama.test",
    "password": "LocalDevPassword123!",
    "device_token": "web-owner-device"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Logged in successfully.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "tenant_owner",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "tenant_id": 1,
            "name": "Demo Tenant Owner",
            "email": "owner@demo.vetllama.test",
            "phone": "+10000000000",
            "is_primary": true,
            "is_active": true,
            "tenant": {
                "id": 1,
                "name": "Demo Clinic",
                "slug": "demo-clinic",
                "status": "active",
                "is_active": true
            }
        }
    }
}
 

Example response (422):


{
    "success": false,
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "Invalid email or password."
        ]
    }
}
 

Request      

POST api/tenant/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Tenant owner email address. Example: owner@demo.vetllama.test

password   string     

Tenant owner password. Example: LocalDevPassword123!

device_token   string  optional    

Optional device token for future push/session tracking. Example: web-owner-device

Logout

requires authentication

Invalidates the current TenantOwner JWT and closes the latest open auth activity session.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/auth/logout" \
    --header "Authorization: Bearer {TENANT_OWNER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/auth/logout"
);

const headers = {
    "Authorization": "Bearer {TENANT_OWNER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Logged out successfully.",
    "data": null
}
 

Request      

POST api/tenant/auth/logout

Headers

Authorization        

Example: Bearer {TENANT_OWNER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Refresh Token

requires authentication

Exchanges a valid TenantOwner JWT for a fresh TenantOwner JWT.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/auth/refresh" \
    --header "Authorization: Bearer {TENANT_OWNER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/auth/refresh"
);

const headers = {
    "Authorization": "Bearer {TENANT_OWNER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Token refreshed successfully.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "tenant_owner",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "tenant_id": 1,
            "email": "owner@demo.vetllama.test"
        }
    }
}
 

Request      

POST api/tenant/auth/refresh

Headers

Authorization        

Example: Bearer {TENANT_OWNER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Current Profile

requires authentication

Returns the authenticated tenant owner and their tenant summary.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/auth/me" \
    --header "Authorization: Bearer {TENANT_OWNER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/auth/me"
);

const headers = {
    "Authorization": "Bearer {TENANT_OWNER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Profile fetched successfully.",
    "data": {
        "id": 1,
        "tenant_id": 1,
        "name": "Demo Tenant Owner",
        "email": "owner@demo.vetllama.test",
        "phone": "+10000000000",
        "is_primary": true,
        "is_active": true,
        "tenant": {
            "id": 1,
            "name": "Demo Clinic",
            "slug": "demo-clinic",
            "status": "active",
            "is_active": true
        }
    }
}
 

Request      

GET api/tenant/auth/me

Headers

Authorization        

Example: Bearer {TENANT_OWNER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Onboarding / Templates

List Templates

requires authentication

Lists active website templates available for the tenant owner to select.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/templates" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/templates"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/templates

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Template Details

requires authentication

Returns the selected template definition, including the schema used by the owner setup UI.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/templates/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/templates/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/templates/{template_id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

template_id   integer     

The ID of the template. Example: 16

Select Template

requires authentication

Assigns one active template to the authenticated tenant.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/templates/selection" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"template_id\": \"architecto\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/templates/selection"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "template_id": "architecto"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/templates/selection

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

template_id   string     

The id of an existing record in the templates table. Example: architecto

Onboarding / Profile

Get Tenant Profile

requires authentication

Returns the authenticated tenant's basic profile and onboarding status fields.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/profile" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/profile"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/profile

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Tenant Profile

requires authentication

Updates owner-facing business profile fields such as display name, slug, contact details, and onboarding status.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/profile" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"business_name\": \"n\",
    \"slug\": \"g\",
    \"email\": \"rowan.gulgowski@example.com\",
    \"primary_phone\": \"d\",
    \"secondary_phone\": \"l\",
    \"short_bio\": \"architecto\",
    \"support_email\": \"zbailey@example.net\",
    \"support_phone\": \"i\",
    \"status\": \"pending\",
    \"onboarding_status\": \"in_progress\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/profile"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "business_name": "n",
    "slug": "g",
    "email": "rowan.gulgowski@example.com",
    "primary_phone": "d",
    "secondary_phone": "l",
    "short_bio": "architecto",
    "support_email": "zbailey@example.net",
    "support_phone": "i",
    "status": "pending",
    "onboarding_status": "in_progress"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/profile

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string  optional    

Must not be greater than 255 characters. Example: b

business_name   string  optional    

Must not be greater than 255 characters. Example: n

slug   string  optional    

Must not be greater than 255 characters. Example: g

email   string  optional    

Must be a valid email address. Must not be greater than 255 characters. Example: rowan.gulgowski@example.com

primary_phone   string  optional    

Must not be greater than 50 characters. Example: d

secondary_phone   string  optional    

Must not be greater than 50 characters. Example: l

short_bio   string  optional    

Example: architecto

support_email   string  optional    

Must be a valid email address. Must not be greater than 255 characters. Example: zbailey@example.net

support_phone   string  optional    

Must not be greater than 50 characters. Example: i

status   string  optional    

Example: pending

Must be one of:
  • pending
  • active
  • paused
onboarding_status   string  optional    

Example: in_progress

Must be one of:
  • not_started
  • in_progress
  • completed

Onboarding / Branding

Get Branding

requires authentication

Returns tenant branding assets and colors used by the public website.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/branding" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/branding"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/branding

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Branding

requires authentication

Updates tenant colors, social links, and branding media. Send multipart/form-data with logo, favicon, profile_photo, or banner_files to store assets on the configured filesystem disk, typically S3.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/branding" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "primary_color=#2563eb"\
    --form "secondary_color=#14b8a6"\
    --form "logo_url=http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"\
    --form "favicon_url=https://www.runte.com/ab-provident-perspiciatis-quo-omnis-nostrum-aut-adipisci"\
    --form "profile_photo_url=https://cronin.com/incidunt-iure-odit-et-et-modi-ipsum.html"\
    --form "social_links[instagram]=https://instagram.com/pawscare"\
    --form "logo=@/tmp/phpiKtxKX" \
    --form "favicon=@/tmp/phpegnmH9" \
    --form "profile_photo=@/tmp/phpg8nALD" \
    --form "banner_files[]=@/tmp/phpO2Kezd" 
const url = new URL(
    "https://api.vetllama.com/api/tenant/branding"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('primary_color', '#2563eb');
body.append('secondary_color', '#14b8a6');
body.append('logo_url', 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html');
body.append('favicon_url', 'https://www.runte.com/ab-provident-perspiciatis-quo-omnis-nostrum-aut-adipisci');
body.append('profile_photo_url', 'https://cronin.com/incidunt-iure-odit-et-et-modi-ipsum.html');
body.append('social_links[instagram]', 'https://instagram.com/pawscare');
body.append('logo', document.querySelector('input[name="logo"]').files[0]);
body.append('favicon', document.querySelector('input[name="favicon"]').files[0]);
body.append('profile_photo', document.querySelector('input[name="profile_photo"]').files[0]);
body.append('banner_files[]', document.querySelector('input[name="banner_files[]"]').files[0]);

fetch(url, {
    method: "PUT",
    headers,
    body,
}).then(response => response.json());

Request      

PUT api/tenant/branding

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

primary_color   string  optional    

Primary brand color. Must not be greater than 20 characters. Example: #2563eb

secondary_color   string  optional    

Secondary brand color. Must not be greater than 20 characters. Example: #14b8a6

logo_url   string  optional    

Must not be greater than 2048 characters. Example: http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html

favicon_url   string  optional    

Must not be greater than 2048 characters. Example: https://www.runte.com/ab-provident-perspiciatis-quo-omnis-nostrum-aut-adipisci

profile_photo_url   string  optional    

Must not be greater than 2048 characters. Example: https://cronin.com/incidunt-iure-odit-et-et-modi-ipsum.html

logo   file  optional    

Logo image uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 4096 kilobytes. Example: /tmp/phpiKtxKX

favicon   file  optional    

Favicon image uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 1024 kilobytes. Example: /tmp/phpegnmH9

profile_photo   file  optional    

Owner or business profile image uploaded to the configured filesystem disk. Must be an image. Must not be greater than 4096 kilobytes. Example: /tmp/phpg8nALD

banner_images   object  optional    
banner_files   file[]  optional    

Must be a file. Must not be greater than 8192 kilobytes.

social_links   object  optional    

Public social profile links.

Update Branding

requires authentication

Updates tenant colors, social links, and branding media. Send multipart/form-data with logo, favicon, profile_photo, or banner_files to store assets on the configured filesystem disk, typically S3.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/branding" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "primary_color=#2563eb"\
    --form "secondary_color=#14b8a6"\
    --form "logo_url=http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"\
    --form "favicon_url=https://www.runte.com/ab-provident-perspiciatis-quo-omnis-nostrum-aut-adipisci"\
    --form "profile_photo_url=https://cronin.com/incidunt-iure-odit-et-et-modi-ipsum.html"\
    --form "social_links[instagram]=https://instagram.com/pawscare"\
    --form "logo=@/tmp/phpOl57M3" \
    --form "favicon=@/tmp/phpJkgjvp" \
    --form "profile_photo=@/tmp/phpJNbkz9" \
    --form "banner_files[]=@/tmp/phpgBjdxb" 
const url = new URL(
    "https://api.vetllama.com/api/tenant/branding"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('primary_color', '#2563eb');
body.append('secondary_color', '#14b8a6');
body.append('logo_url', 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html');
body.append('favicon_url', 'https://www.runte.com/ab-provident-perspiciatis-quo-omnis-nostrum-aut-adipisci');
body.append('profile_photo_url', 'https://cronin.com/incidunt-iure-odit-et-et-modi-ipsum.html');
body.append('social_links[instagram]', 'https://instagram.com/pawscare');
body.append('logo', document.querySelector('input[name="logo"]').files[0]);
body.append('favicon', document.querySelector('input[name="favicon"]').files[0]);
body.append('profile_photo', document.querySelector('input[name="profile_photo"]').files[0]);
body.append('banner_files[]', document.querySelector('input[name="banner_files[]"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Request      

POST api/tenant/branding

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

primary_color   string  optional    

Primary brand color. Must not be greater than 20 characters. Example: #2563eb

secondary_color   string  optional    

Secondary brand color. Must not be greater than 20 characters. Example: #14b8a6

logo_url   string  optional    

Must not be greater than 2048 characters. Example: http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html

favicon_url   string  optional    

Must not be greater than 2048 characters. Example: https://www.runte.com/ab-provident-perspiciatis-quo-omnis-nostrum-aut-adipisci

profile_photo_url   string  optional    

Must not be greater than 2048 characters. Example: https://cronin.com/incidunt-iure-odit-et-et-modi-ipsum.html

logo   file  optional    

Logo image uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 4096 kilobytes. Example: /tmp/phpOl57M3

favicon   file  optional    

Favicon image uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 1024 kilobytes. Example: /tmp/phpJkgjvp

profile_photo   file  optional    

Owner or business profile image uploaded to the configured filesystem disk. Must be an image. Must not be greater than 4096 kilobytes. Example: /tmp/phpJNbkz9

banner_images   object  optional    
banner_files   file[]  optional    

Must be a file. Must not be greater than 8192 kilobytes.

social_links   object  optional    

Public social profile links.

Onboarding / Public Config

Get Public Config

requires authentication

Returns template-driven public website content such as homepage sections, FAQs, policies, SEO metadata, and public toggles.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/public-config" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/public-config"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/public-config

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Public Config

requires authentication

Updates flexible public website content for the authenticated tenant.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/public-config" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"contact_details\": {
        \"email\": \"gbailey@example.net\",
        \"phone\": \"m\"
    },
    \"is_published\": true,
    \"banners\": [
        {
            \"url\": \"https:\\/\\/www.gulgowski.com\\/nihil-accusantium-harum-mollitia-modi-deserunt\"
        }
    ],
    \"faq\": [
        {
            \"question\": \"architecto\",
            \"answer\": \"architecto\"
        }
    ]
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/public-config"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "contact_details": {
        "email": "gbailey@example.net",
        "phone": "m"
    },
    "is_published": true,
    "banners": [
        {
            "url": "https:\/\/www.gulgowski.com\/nihil-accusantium-harum-mollitia-modi-deserunt"
        }
    ],
    "faq": [
        {
            "question": "architecto",
            "answer": "architecto"
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/public-config

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

contact_details   object  optional    
email   string  optional    

Must be a valid email address. Example: gbailey@example.net

phone   string  optional    

Must not be greater than 30 characters. Example: m

homepage_content   object  optional    
banners   object[]  optional    
url   string  optional    

Must be a valid URL. Example: https://www.gulgowski.com/nihil-accusantium-harum-mollitia-modi-deserunt

faq   object[]  optional    
question   string  optional    

Example: architecto

answer   string  optional    

Example: architecto

services   object  optional    
privacy_policy   object  optional    
terms_conditions   object  optional    
seo_meta   object  optional    
public_toggles   object  optional    
settings   object  optional    
is_published   boolean  optional    

Example: true

Upload Public Config Media

requires authentication

Uploads template-driven public website media such as hero, banner, and section images to the configured filesystem disk.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/public-config/media" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "section_key=about"\
    --form "hero_image=@/tmp/phpkFmAe8" \
    --form "banner_images[]=@/tmp/phpU1usXA" \
    --form "section_images[]=@/tmp/phpPnA6cP" 
const url = new URL(
    "https://api.vetllama.com/api/tenant/public-config/media"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('section_key', 'about');
body.append('hero_image', document.querySelector('input[name="hero_image"]').files[0]);
body.append('banner_images[]', document.querySelector('input[name="banner_images[]"]').files[0]);
body.append('section_images[]', document.querySelector('input[name="section_images[]"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Request      

POST api/tenant/public-config/media

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

hero_image   file  optional    

Hero image stored under tenant public-config media. Must be an image. Must not be greater than 8192 kilobytes. Example: /tmp/phpkFmAe8

banner_images   file[]  optional    

Must be a file. Must not be greater than 8192 kilobytes.

section_images   file[]  optional    

Must be a file. Must not be greater than 8192 kilobytes.

section_key   string  optional    

Optional section key for uploaded section images. Must not be greater than 100 characters. Example: about

Validate Required Fields

requires authentication

Checks whether the current public content satisfies the selected template's required fields and returns any missing field keys.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/public-config/validate-required-fields" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/public-config/validate-required-fields"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/public-config/validate-required-fields

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Onboarding / Domains

List Host Mappings

requires authentication

Lists all website hosts mapped to the authenticated tenant, including multiple subdomains and multiple custom domains.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/domains" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/domains

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Add Host Mapping

requires authentication

Adds a tenant website host mapping. Use type=subdomain with subdomain, or type=custom_domain with host.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/domains" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"host\": \"paws-care.com\",
    \"domain\": \"b\",
    \"subdomain\": \"paws-care\",
    \"label\": \"n\",
    \"type\": \"subdomain\",
    \"is_primary\": true,
    \"is_active\": true
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "host": "paws-care.com",
    "domain": "b",
    "subdomain": "paws-care",
    "label": "n",
    "type": "subdomain",
    "is_primary": true,
    "is_active": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/domains

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

host   string  optional    

Full custom domain host. Use for custom_domain mappings. Must not be greater than 255 characters. Example: paws-care.com

domain   string  optional    

Must not be greater than 255 characters. Example: b

subdomain   string  optional    

Subdomain label. The API stores it as a full host using the configured tenant base domain. Must match the regex /^[a-z0-9]+(?:-[a-z0-9]+)*$/. Must be at least 3 characters. Must not be greater than 63 characters. Example: paws-care

label   string  optional    

Must match the regex /^[a-z0-9]+(?:-[a-z0-9]+)*$/. Must be at least 3 characters. Must not be greater than 63 characters. Example: n

type   string  optional    

Host mapping type: subdomain or custom_domain. Example: subdomain

Must be one of:
  • subdomain
  • custom_domain
is_primary   boolean  optional    

Whether this mapped host should be the tenant primary public host. Example: true

is_active   boolean  optional    

Whether this mapped host can resolve public tenant configuration. Example: true

Sync Host Mappings

requires authentication

Replaces the tenant's editable website host mappings from frontend-provided arrays. Existing mappings omitted from subdomains and domains are deactivated. Existing submitted mappings are reactivated.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/domains/sync" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"subdomains\": [
        \"paws-care\",
        \"londonpets\"
    ],
    \"domains\": [
        \"pawscare.com\",
        \"telepaws.co.uk\"
    ],
    \"primary_host\": \"paws-care.vetllama.test\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains/sync"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subdomains": [
        "paws-care",
        "londonpets"
    ],
    "domains": [
        "pawscare.com",
        "telepaws.co.uk"
    ],
    "primary_host": "paws-care.vetllama.test"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Host mappings synced.",
    "data": [
        {
            "id": 1,
            "host": "paws-care.vetllama.test",
            "domain": "paws-care.vetllama.test",
            "label": "paws-care",
            "subdomain": "paws-care",
            "type": "subdomain",
            "is_primary": true,
            "is_active": true,
            "is_verified": true
        },
        {
            "id": 2,
            "host": "pawscare.com",
            "domain": "pawscare.com",
            "label": null,
            "subdomain": null,
            "type": "custom_domain",
            "is_primary": false,
            "is_active": true,
            "is_verified": false
        }
    ]
}
 

Request      

PUT api/tenant/domains/sync

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

subdomains   string[]     

Complete list of subdomain labels to keep mapped.

domains   string[]     

Complete list of custom domains to keep mapped.

primary_host   string  optional    

Optional primary host. May be a full host or one of the submitted subdomain labels. Example: paws-care.vetllama.test

Update Host Mapping

requires authentication

Updates host mapping status, verification metadata, or primary flag for a tenant-owned website host.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/domains/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"host\": \"paws-care.com\",
    \"domain\": \"b\",
    \"subdomain\": \"paws-care\",
    \"label\": \"n\",
    \"type\": \"subdomain\",
    \"is_primary\": true,
    \"is_active\": true
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "host": "paws-care.com",
    "domain": "b",
    "subdomain": "paws-care",
    "label": "n",
    "type": "subdomain",
    "is_primary": true,
    "is_active": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/domains/{domain_id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

domain_id   integer     

The ID of the domain. Example: 16

Body Parameters

host   string  optional    

Full custom domain host. Use for custom_domain mappings. Must not be greater than 255 characters. Example: paws-care.com

domain   string  optional    

Must not be greater than 255 characters. Example: b

subdomain   string  optional    

Subdomain label. The API stores it as a full host using the configured tenant base domain. Must match the regex /^[a-z0-9]+(?:-[a-z0-9]+)*$/. Must be at least 3 characters. Must not be greater than 63 characters. Example: paws-care

label   string  optional    

Must match the regex /^[a-z0-9]+(?:-[a-z0-9]+)*$/. Must be at least 3 characters. Must not be greater than 63 characters. Example: n

type   string  optional    

Host mapping type: subdomain or custom_domain. Example: subdomain

Must be one of:
  • subdomain
  • custom_domain
is_primary   boolean  optional    

Whether this mapped host should be the tenant primary public host. Example: true

is_active   boolean  optional    

Whether this mapped host can resolve public tenant configuration. Example: true

Get Verification Details

requires authentication

Returns DNS TXT verification instructions and current status for a tenant custom domain.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/domains/16/verification" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains/16/verification"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/domains/{domain_id}/verification

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

domain_id   integer     

The ID of the domain. Example: 16

Check Domain Verification

requires authentication

Triggers a DNS TXT verification check for a tenant custom domain and updates its verification state.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/domains/16/verification/check" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains/16/verification/check"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/domains/{domain_id}/verification/check

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

domain_id   integer     

The ID of the domain. Example: 16

Set Primary Host Mapping

requires authentication

Marks this mapped host as the tenant's primary public host and clears other primary flags.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/domains/16/primary" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains/16/primary"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/domains/{domain_id}/primary

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

domain_id   integer     

The ID of the domain. Example: 16

Deactivate Host Mapping

requires authentication

Deactivates a tenant website host mapping without removing historical configuration.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/domains/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/domains/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/domains/{domain_id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

domain_id   integer     

The ID of the domain. Example: 16

Onboarding / Locations

List Locations

requires authentication

Lists physical visit locations for the authenticated tenant.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/locations" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/locations"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/locations

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Location

requires authentication

Creates a physical location that can be linked to physical visit services.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/locations" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"address_line_1\": \"n\",
    \"address_line_2\": \"g\",
    \"city\": \"z\",
    \"state\": \"m\",
    \"country\": \"i\",
    \"postal_code\": \"y\",
    \"latitude\": -89,
    \"longitude\": -179,
    \"contact_phone\": \"l\",
    \"instructions\": \"architecto\",
    \"is_primary\": false,
    \"is_active\": false
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/locations"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "address_line_1": "n",
    "address_line_2": "g",
    "city": "z",
    "state": "m",
    "country": "i",
    "postal_code": "y",
    "latitude": -89,
    "longitude": -179,
    "contact_phone": "l",
    "instructions": "architecto",
    "is_primary": false,
    "is_active": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/locations

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Must not be greater than 255 characters. Example: b

address_line_1   string     

Must not be greater than 255 characters. Example: n

address_line_2   string  optional    

Must not be greater than 255 characters. Example: g

city   string     

Must not be greater than 255 characters. Example: z

state   string  optional    

Must not be greater than 255 characters. Example: m

country   string     

Must not be greater than 255 characters. Example: i

postal_code   string  optional    

Must not be greater than 30 characters. Example: y

latitude   number  optional    

Must be between -90 and 90. Example: -89

longitude   number  optional    

Must be between -180 and 180. Example: -179

contact_phone   string  optional    

Must not be greater than 50 characters. Example: l

instructions   string  optional    

Example: architecto

is_primary   boolean  optional    

Example: false

is_active   boolean  optional    

Example: false

Show Location

requires authentication

Returns one tenant-owned location.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/locations/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/locations/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/locations/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the location. Example: 16

Update Location

requires authentication

Updates a tenant-owned physical location.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/locations/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"address_line_1\": \"n\",
    \"address_line_2\": \"g\",
    \"city\": \"z\",
    \"state\": \"m\",
    \"country\": \"i\",
    \"postal_code\": \"y\",
    \"latitude\": -89,
    \"longitude\": -179,
    \"contact_phone\": \"l\",
    \"instructions\": \"architecto\",
    \"is_primary\": true,
    \"is_active\": false
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/locations/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "address_line_1": "n",
    "address_line_2": "g",
    "city": "z",
    "state": "m",
    "country": "i",
    "postal_code": "y",
    "latitude": -89,
    "longitude": -179,
    "contact_phone": "l",
    "instructions": "architecto",
    "is_primary": true,
    "is_active": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/locations/{id}

PATCH api/tenant/locations/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the location. Example: 16

Body Parameters

name   string     

Must not be greater than 255 characters. Example: b

address_line_1   string     

Must not be greater than 255 characters. Example: n

address_line_2   string  optional    

Must not be greater than 255 characters. Example: g

city   string     

Must not be greater than 255 characters. Example: z

state   string  optional    

Must not be greater than 255 characters. Example: m

country   string     

Must not be greater than 255 characters. Example: i

postal_code   string  optional    

Must not be greater than 30 characters. Example: y

latitude   number  optional    

Must be between -90 and 90. Example: -89

longitude   number  optional    

Must be between -180 and 180. Example: -179

contact_phone   string  optional    

Must not be greater than 50 characters. Example: l

instructions   string  optional    

Example: architecto

is_primary   boolean  optional    

Example: true

is_active   boolean  optional    

Example: false

Delete Location

requires authentication

Deactivates a tenant-owned location.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/locations/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/locations/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/locations/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the location. Example: 16

Onboarding / Services

List Services

requires authentication

Lists tenant service offerings, including their duration and pricing options.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/services" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/services

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Service

requires authentication

Creates a service offering for physical visits, telehealth, or instant consult configuration.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/services" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"physical_visit\",
    \"name\": \"b\",
    \"description\": \"Eius et animi quos velit et.\",
    \"is_enabled\": false,
    \"delivery_mode\": \"v\",
    \"sort_order\": 42,
    \"status\": \"active\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "physical_visit",
    "name": "b",
    "description": "Eius et animi quos velit et.",
    "is_enabled": false,
    "delivery_mode": "v",
    "sort_order": 42,
    "status": "active"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/services

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

type   string     

Example: physical_visit

Must be one of:
  • physical_visit
  • telehealth
  • instant_consult
name   string     

Must not be greater than 255 characters. Example: b

description   string  optional    

Example: Eius et animi quos velit et.

is_enabled   boolean  optional    

Example: false

location_id   string  optional    

This field is required when type is physical_visit. The id of an existing record in the tenant_locations table.

delivery_mode   string  optional    

Must not be greater than 255 characters. Example: v

metadata   object  optional    
sort_order   integer  optional    

Must be at least 0. Example: 42

status   string  optional    

Example: active

Must be one of:
  • active
  • draft
  • archived

Show Service

requires authentication

Returns one tenant-owned service offering with duration and pricing options.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/services/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/services/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the service. Example: 16

Update Service

requires authentication

Updates a tenant-owned service offering.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/services/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"instant_consult\",
    \"name\": \"b\",
    \"description\": \"Eius et animi quos velit et.\",
    \"is_enabled\": true,
    \"delivery_mode\": \"v\",
    \"sort_order\": 42,
    \"status\": \"active\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "instant_consult",
    "name": "b",
    "description": "Eius et animi quos velit et.",
    "is_enabled": true,
    "delivery_mode": "v",
    "sort_order": 42,
    "status": "active"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/services/{id}

PATCH api/tenant/services/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the service. Example: 16

Body Parameters

type   string     

Example: instant_consult

Must be one of:
  • physical_visit
  • telehealth
  • instant_consult
name   string     

Must not be greater than 255 characters. Example: b

description   string  optional    

Example: Eius et animi quos velit et.

is_enabled   boolean  optional    

Example: true

location_id   string  optional    

This field is required when type is physical_visit. The id of an existing record in the tenant_locations table.

delivery_mode   string  optional    

Must not be greater than 255 characters. Example: v

metadata   object  optional    
sort_order   integer  optional    

Must be at least 0. Example: 42

status   string  optional    

Example: active

Must be one of:
  • active
  • draft
  • archived

Delete Service

requires authentication

Archives a tenant-owned service offering.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/services/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/services/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the service. Example: 16

Onboarding / Service Durations & Pricing

List Service Durations

requires authentication

Lists pricing and duration options for a tenant-owned service.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/services/16/durations" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16/durations"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/services/{service_id}/durations

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

service_id   integer     

The ID of the service. Example: 16

Create Service Duration

requires authentication

Creates a pricing and duration option for a tenant-owned service.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/services/16/durations" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"duration_minutes\": 1,
    \"price\": 39,
    \"currency\": \"gzm\",
    \"is_default\": false,
    \"is_active\": true
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16/durations"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "duration_minutes": 1,
    "price": 39,
    "currency": "gzm",
    "is_default": false,
    "is_active": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/services/{service_id}/durations

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

service_id   integer     

The ID of the service. Example: 16

Body Parameters

duration_minutes   integer     

Must be at least 5. Must not be greater than 480. Example: 1

price   number     

Must be at least 0. Example: 39

currency   string     

Must be 3 characters. Example: gzm

is_default   boolean  optional    

Example: false

is_active   boolean  optional    

Example: true

Update Service Duration

requires authentication

Updates a pricing and duration option for a tenant-owned service.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/services/16/durations/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"duration_minutes\": 1,
    \"price\": 39,
    \"currency\": \"gzm\",
    \"is_default\": true,
    \"is_active\": true
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16/durations/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "duration_minutes": 1,
    "price": 39,
    "currency": "gzm",
    "is_default": true,
    "is_active": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/services/{service_id}/durations/{duration_id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

service_id   integer     

The ID of the service. Example: 16

duration_id   integer     

The ID of the duration. Example: 16

Body Parameters

duration_minutes   integer     

Must be at least 5. Must not be greater than 480. Example: 1

price   number     

Must be at least 0. Example: 39

currency   string     

Must be 3 characters. Example: gzm

is_default   boolean  optional    

Example: true

is_active   boolean  optional    

Example: true

Delete Service Duration

requires authentication

Deactivates a pricing and duration option.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/services/16/durations/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/services/16/durations/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/services/{service_id}/durations/{duration_id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

service_id   integer     

The ID of the service. Example: 16

duration_id   integer     

The ID of the duration. Example: 16

Onboarding / Availability

List Availability

requires authentication

Lists weekly recurring availability windows for the authenticated tenant.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/availability" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/availability

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Availability

requires authentication

Creates a weekly recurring availability window for a tenant, service, or location.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/availability" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"day_of_week\": 1,
    \"start_time\": \"11:23\",
    \"end_time\": \"2052-06-26\",
    \"timezone\": \"Asia\\/Ulaanbaatar\",
    \"is_active\": false
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "day_of_week": 1,
    "start_time": "11:23",
    "end_time": "2052-06-26",
    "timezone": "Asia\/Ulaanbaatar",
    "is_active": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/availability

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

service_offering_id   string  optional    

The id of an existing record in the service_offerings table.

location_id   string  optional    

The id of an existing record in the tenant_locations table.

day_of_week   integer     

Must be between 0 and 6. Example: 1

start_time   string     

Must be a valid date in the format H:i. Example: 11:23

end_time   string     

Must be a valid date in the format H:i. Must be a date after start_time. Example: 2052-06-26

timezone   string     

Must be a valid time zone, such as Africa/Accra. Example: Asia/Ulaanbaatar

is_active   boolean  optional    

Example: false

Update Availability

requires authentication

Updates a weekly recurring availability window.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/availability/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"day_of_week\": 1,
    \"start_time\": \"11:23\",
    \"end_time\": \"2052-06-26\",
    \"timezone\": \"Asia\\/Ulaanbaatar\",
    \"is_active\": true
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "day_of_week": 1,
    "start_time": "11:23",
    "end_time": "2052-06-26",
    "timezone": "Asia\/Ulaanbaatar",
    "is_active": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/availability/{id}

PATCH api/tenant/availability/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the availability. Example: 16

Body Parameters

service_offering_id   string  optional    

The id of an existing record in the service_offerings table.

location_id   string  optional    

The id of an existing record in the tenant_locations table.

day_of_week   integer     

Must be between 0 and 6. Example: 1

start_time   string     

Must be a valid date in the format H:i. Example: 11:23

end_time   string     

Must be a valid date in the format H:i. Must be a date after start_time. Example: 2052-06-26

timezone   string     

Must be a valid time zone, such as Africa/Accra. Example: Asia/Ulaanbaatar

is_active   boolean  optional    

Example: true

Delete Availability

requires authentication

Deactivates a weekly recurring availability window.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/availability/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/availability/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the availability. Example: 16

Onboarding / Availability Exceptions

List Availability Exceptions

requires authentication

Lists blocked dates or one-off availability exceptions for the authenticated tenant.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/availability-exceptions" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability-exceptions"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/availability-exceptions

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Availability Exception

requires authentication

Creates a full-day or partial-day blocked date for a tenant, service, or location.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/availability-exceptions" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"specific_date\": \"2026-06-03T11:23:05\",
    \"start_time\": \"11:23\",
    \"end_time\": \"2052-06-26\",
    \"reason\": \"n\",
    \"full_day\": false
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability-exceptions"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "specific_date": "2026-06-03T11:23:05",
    "start_time": "11:23",
    "end_time": "2052-06-26",
    "reason": "n",
    "full_day": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/availability-exceptions

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

service_offering_id   string  optional    

The id of an existing record in the service_offerings table.

location_id   string  optional    

The id of an existing record in the tenant_locations table.

specific_date   string     

Must be a valid date. Example: 2026-06-03T11:23:05

start_time   string  optional    

This field is required when full_day is false. Must be a valid date in the format H:i. Example: 11:23

end_time   string  optional    

This field is required when full_day is false. Must be a valid date in the format H:i. Must be a date after start_time. Example: 2052-06-26

reason   string  optional    

Must not be greater than 255 characters. Example: n

full_day   boolean  optional    

Example: false

Update Availability Exception

requires authentication

Updates a blocked date or availability exception.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/availability-exceptions/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"specific_date\": \"2026-06-03T11:23:05\",
    \"start_time\": \"11:23\",
    \"end_time\": \"2052-06-26\",
    \"reason\": \"n\",
    \"full_day\": false
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability-exceptions/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "specific_date": "2026-06-03T11:23:05",
    "start_time": "11:23",
    "end_time": "2052-06-26",
    "reason": "n",
    "full_day": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/availability-exceptions/{id}

PATCH api/tenant/availability-exceptions/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the availability exception. Example: 16

Body Parameters

service_offering_id   string  optional    

The id of an existing record in the service_offerings table.

location_id   string  optional    

The id of an existing record in the tenant_locations table.

specific_date   string     

Must be a valid date. Example: 2026-06-03T11:23:05

start_time   string  optional    

This field is required when full_day is false. Must be a valid date in the format H:i. Example: 11:23

end_time   string  optional    

This field is required when full_day is false. Must be a valid date in the format H:i. Must be a date after start_time. Example: 2052-06-26

reason   string  optional    

Must not be greater than 255 characters. Example: n

full_day   boolean  optional    

Example: false

Delete Availability Exception

requires authentication

Deletes a blocked date or availability exception.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/availability-exceptions/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/availability-exceptions/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/availability-exceptions/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the availability exception. Example: 16

Onboarding / Booking Policies

Get Booking Policies

requires authentication

Returns booking rule configuration for the authenticated tenant.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/booking-policies" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/booking-policies"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/booking-policies

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Booking Policies

requires authentication

Updates booking rule configuration such as advance notice, buffers, cancellation, and reschedule settings.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/booking-policies" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"timezone\": \"Asia\\/Yekaterinburg\",
    \"min_advance_notice_minutes\": 39,
    \"max_advance_booking_days\": 7,
    \"buffer_before_minutes\": 12,
    \"buffer_after_minutes\": 77,
    \"cancellation_cutoff_hours\": 8,
    \"reschedule_cutoff_hours\": 76,
    \"slot_interval_minutes\": 14,
    \"default_booking_status\": \"pending\",
    \"allow_cancellation\": false,
    \"allow_reschedule\": false
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/booking-policies"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "timezone": "Asia\/Yekaterinburg",
    "min_advance_notice_minutes": 39,
    "max_advance_booking_days": 7,
    "buffer_before_minutes": 12,
    "buffer_after_minutes": 77,
    "cancellation_cutoff_hours": 8,
    "reschedule_cutoff_hours": 76,
    "slot_interval_minutes": 14,
    "default_booking_status": "pending",
    "allow_cancellation": false,
    "allow_reschedule": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/booking-policies

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

timezone   string     

Must be a valid time zone, such as Africa/Accra. Example: Asia/Yekaterinburg

min_advance_notice_minutes   integer     

Must be at least 0. Example: 39

max_advance_booking_days   integer     

Must be at least 1. Must not be greater than 730. Example: 7

buffer_before_minutes   integer     

Must be at least 0. Example: 12

buffer_after_minutes   integer     

Must be at least 0. Example: 77

cancellation_cutoff_hours   integer  optional    

Must be at least 0. Example: 8

reschedule_cutoff_hours   integer  optional    

Must be at least 0. Example: 76

slot_interval_minutes   integer  optional    

Must be at least 5. Example: 14

default_booking_status   string     

Example: pending

Must be one of:
  • pending
  • confirmed
allow_cancellation   boolean     

Example: false

allow_reschedule   boolean     

Example: false

Onboarding / Licenses & Certifications

List Licenses

requires authentication

Lists tenant licenses, certifications, and prescribing eligibility metadata.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/licenses" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/licenses"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/licenses

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create License

requires authentication

Creates a tenant license or certification record. Send multipart/form-data with file to upload a PDF or image document to the configured filesystem disk.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/licenses" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "type=professional"\
    --form "title=Veterinary Practice License"\
    --form "license_number=VET-2026-001"\
    --form "issuing_authority=b"\
    --form "issue_date=2026-06-03T11:23:05"\
    --form "expiry_date=2052-06-26"\
    --form "can_prescribe="\
    --form "notes=architecto"\
    --form "is_active="\
    --form "verification_status=pending"\
    --form "file=@/tmp/phpoCktT6" 
const url = new URL(
    "https://api.vetllama.com/api/tenant/licenses"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('type', 'professional');
body.append('title', 'Veterinary Practice License');
body.append('license_number', 'VET-2026-001');
body.append('issuing_authority', 'b');
body.append('issue_date', '2026-06-03T11:23:05');
body.append('expiry_date', '2052-06-26');
body.append('can_prescribe', '');
body.append('notes', 'architecto');
body.append('is_active', '');
body.append('verification_status', 'pending');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Request      

POST api/tenant/licenses

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

type   string     

License or certification type. Must not be greater than 100 characters. Example: professional

title   string     

Display title for the license or certification. Must not be greater than 255 characters. Example: Veterinary Practice License

license_number   string  optional    

Optional license or certificate number. Must not be greater than 255 characters. Example: VET-2026-001

issuing_authority   string  optional    

Must not be greater than 255 characters. Example: b

issue_date   string  optional    

Must be a valid date. Example: 2026-06-03T11:23:05

expiry_date   string  optional    

Must be a valid date. Must be a date after or equal to issue_date. Example: 2052-06-26

can_prescribe   boolean  optional    

Example: false

file   file  optional    

Optional PDF or image document uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 10240 kilobytes. Example: /tmp/phpoCktT6

notes   string  optional    

Example: architecto

is_active   boolean  optional    

Example: false

verification_status   string  optional    

Internal verification status. Example: pending

Must be one of:
  • pending
  • verified
  • rejected

Update License

requires authentication

Updates a tenant license or certification record. Send multipart/form-data with file to replace the stored document.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/licenses/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "type=professional"\
    --form "title=Veterinary Practice License"\
    --form "license_number=VET-2026-001"\
    --form "issuing_authority=b"\
    --form "issue_date=2026-06-03T11:23:05"\
    --form "expiry_date=2052-06-26"\
    --form "can_prescribe=1"\
    --form "notes=architecto"\
    --form "is_active="\
    --form "verification_status=pending"\
    --form "file=@/tmp/phpLQC814" 
const url = new URL(
    "https://api.vetllama.com/api/tenant/licenses/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('type', 'professional');
body.append('title', 'Veterinary Practice License');
body.append('license_number', 'VET-2026-001');
body.append('issuing_authority', 'b');
body.append('issue_date', '2026-06-03T11:23:05');
body.append('expiry_date', '2052-06-26');
body.append('can_prescribe', '1');
body.append('notes', 'architecto');
body.append('is_active', '');
body.append('verification_status', 'pending');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "PUT",
    headers,
    body,
}).then(response => response.json());

Request      

PUT api/tenant/licenses/{id}

PATCH api/tenant/licenses/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the license. Example: 16

Body Parameters

type   string     

License or certification type. Must not be greater than 100 characters. Example: professional

title   string     

Display title for the license or certification. Must not be greater than 255 characters. Example: Veterinary Practice License

license_number   string  optional    

Optional license or certificate number. Must not be greater than 255 characters. Example: VET-2026-001

issuing_authority   string  optional    

Must not be greater than 255 characters. Example: b

issue_date   string  optional    

Must be a valid date. Example: 2026-06-03T11:23:05

expiry_date   string  optional    

Must be a valid date. Must be a date after or equal to issue_date. Example: 2052-06-26

can_prescribe   boolean  optional    

Example: true

file   file  optional    

Optional PDF or image document uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 10240 kilobytes. Example: /tmp/phpLQC814

notes   string  optional    

Example: architecto

is_active   boolean  optional    

Example: false

verification_status   string  optional    

Internal verification status. Example: pending

Must be one of:
  • pending
  • verified
  • rejected

Delete License

requires authentication

Deactivates a tenant license or certification record.

Example request:
curl --request DELETE \
    "https://api.vetllama.com/api/tenant/licenses/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/licenses/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/tenant/licenses/{id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The ID of the license. Example: 16

Update License

requires authentication

Updates a tenant license or certification record. Send multipart/form-data with file to replace the stored document.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/licenses/16" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "type=professional"\
    --form "title=Veterinary Practice License"\
    --form "license_number=VET-2026-001"\
    --form "issuing_authority=b"\
    --form "issue_date=2026-06-03T11:23:05"\
    --form "expiry_date=2052-06-26"\
    --form "can_prescribe=1"\
    --form "notes=architecto"\
    --form "is_active=1"\
    --form "verification_status=pending"\
    --form "file=@/tmp/phplLk8PB" 
const url = new URL(
    "https://api.vetllama.com/api/tenant/licenses/16"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('type', 'professional');
body.append('title', 'Veterinary Practice License');
body.append('license_number', 'VET-2026-001');
body.append('issuing_authority', 'b');
body.append('issue_date', '2026-06-03T11:23:05');
body.append('expiry_date', '2052-06-26');
body.append('can_prescribe', '1');
body.append('notes', 'architecto');
body.append('is_active', '1');
body.append('verification_status', 'pending');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Request      

POST api/tenant/licenses/{license_id}

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

license_id   integer     

The ID of the license. Example: 16

Body Parameters

type   string     

License or certification type. Must not be greater than 100 characters. Example: professional

title   string     

Display title for the license or certification. Must not be greater than 255 characters. Example: Veterinary Practice License

license_number   string  optional    

Optional license or certificate number. Must not be greater than 255 characters. Example: VET-2026-001

issuing_authority   string  optional    

Must not be greater than 255 characters. Example: b

issue_date   string  optional    

Must be a valid date. Example: 2026-06-03T11:23:05

expiry_date   string  optional    

Must be a valid date. Must be a date after or equal to issue_date. Example: 2052-06-26

can_prescribe   boolean  optional    

Example: true

file   file  optional    

Optional PDF or image document uploaded to the configured filesystem disk, usually S3. Must be a file. Must not be greater than 10240 kilobytes. Example: /tmp/phplLk8PB

notes   string  optional    

Example: architecto

is_active   boolean  optional    

Example: true

verification_status   string  optional    

Internal verification status. Example: pending

Must be one of:
  • pending
  • verified
  • rejected

Onboarding / Payment Configuration

Get Payment Configuration

requires authentication

Returns tenant payment provider configuration and connection status.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/payment-config" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/payment-config"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/payment-config

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Payment Configuration

requires authentication

Updates payment provider configuration. Secrets are encrypted at rest and omitted from responses.

Example request:
curl --request PUT \
    "https://api.vetllama.com/api/tenant/payment-config" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"provider\": \"b\",
    \"mode\": \"live\",
    \"connected_account_id\": \"n\",
    \"publishable_key\": \"g\",
    \"secret_key\": \"architecto\",
    \"webhook_secret\": \"architecto\",
    \"charges_enabled\": false,
    \"payouts_enabled\": true,
    \"details_submitted\": true,
    \"onboarding_completed_at\": \"2026-06-03T11:23:05\",
    \"status\": \"not_connected\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/payment-config"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "provider": "b",
    "mode": "live",
    "connected_account_id": "n",
    "publishable_key": "g",
    "secret_key": "architecto",
    "webhook_secret": "architecto",
    "charges_enabled": false,
    "payouts_enabled": true,
    "details_submitted": true,
    "onboarding_completed_at": "2026-06-03T11:23:05",
    "status": "not_connected"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/tenant/payment-config

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

provider   string     

Must not be greater than 50 characters. Example: b

mode   string     

Example: live

Must be one of:
  • test
  • live
connected_account_id   string  optional    

Must not be greater than 255 characters. Example: n

publishable_key   string  optional    

Must not be greater than 255 characters. Example: g

secret_key   string  optional    

Example: architecto

webhook_secret   string  optional    

Example: architecto

charges_enabled   boolean  optional    

Example: false

payouts_enabled   boolean  optional    

Example: true

details_submitted   boolean  optional    

Example: true

onboarding_completed_at   string  optional    

Must be a valid date. Example: 2026-06-03T11:23:05

status   string     

Example: not_connected

Must be one of:
  • not_connected
  • pending
  • connected
  • disabled
metadata   object  optional    
provider_metadata   object  optional    

Start Stripe Connect Onboarding

requires authentication

Creates or reuses a Stripe connected account and returns an onboarding URL.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/payment-config/stripe/connect" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"return_url\": \"https:\\/\\/owner.vetllama.test\\/settings\\/payments\\/return\",
    \"refresh_url\": \"https:\\/\\/owner.vetllama.test\\/settings\\/payments\\/refresh\",
    \"country\": \"US\"
}"
const url = new URL(
    "https://api.vetllama.com/api/tenant/payment-config/stripe/connect"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "return_url": "https:\/\/owner.vetllama.test\/settings\/payments\/return",
    "refresh_url": "https:\/\/owner.vetllama.test\/settings\/payments\/refresh",
    "country": "US"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/tenant/payment-config/stripe/connect

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

return_url   string     

Frontend URL Stripe should return to after onboarding. Must be a valid URL. Must not be greater than 2048 characters. Example: https://owner.vetllama.test/settings/payments/return

refresh_url   string     

Frontend URL Stripe should use if onboarding link expires. Must be a valid URL. Must not be greater than 2048 characters. Example: https://owner.vetllama.test/settings/payments/refresh

country   string  optional    

Optional ISO-3166 alpha-2 country for the connected account. Must be 2 characters. Example: US

Get Stripe Connection Status

requires authentication

Returns the locally stored Stripe connection status for this tenant.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/payment-config/stripe/status" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/payment-config/stripe/status"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/payment-config/stripe/status

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Refresh Stripe Connection Status

requires authentication

Pulls the latest connected-account status from Stripe and stores it locally.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/payment-config/stripe/refresh" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/payment-config/stripe/refresh"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/payment-config/stripe/refresh

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Disconnect Stripe

requires authentication

Clears the local Stripe connected-account state. This does not delete the Stripe account.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/payment-config/stripe/disconnect" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/payment-config/stripe/disconnect"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/payment-config/stripe/disconnect

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Onboarding / Onboarding Progress

Get Onboarding Status

requires authentication

Returns step-by-step onboarding completion status for the authenticated tenant.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/onboarding/status" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/onboarding/status"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/onboarding/status

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Publish Readiness

requires authentication

Returns whether the authenticated tenant has completed the required setup to publish.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/onboarding/publish-readiness" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/onboarding/publish-readiness"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/onboarding/publish-readiness

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Onboarding / Publish Workflow

Get Publish Status

requires authentication

Returns current tenant publication state and publish readiness details.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/tenant/publish/status" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/publish/status"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Request      

GET api/tenant/publish/status

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Publish Tenant

requires authentication

Publishes the tenant website if all readiness requirements are satisfied.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/publish" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/publish"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/publish

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Unpublish Tenant

requires authentication

Removes the tenant website from public published status without deleting configuration.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/unpublish" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/unpublish"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/unpublish

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Pause Tenant

requires authentication

Pauses public tenant availability while keeping setup data intact.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/tenant/pause" \
    --header "Authorization: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/tenant/pause"
);

const headers = {
    "Authorization": "Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/tenant/pause

Headers

Authorization        

Example: Bearer {ADMIN_OR_TENANT_OWNER_OR_END_USER_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

User

Auth

Requests a one-time magic sign-in link for an existing EndUser under the resolved tenant. This endpoint never creates users and always returns a generic success response so email existence is not exposed.

Use Host: demo.vetllama.test in local development when testing against seeded tenant data.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/user/auth/magic-link/request" \
    --header "Host: demo.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jane@example.com\"
}"
const url = new URL(
    "https://api.vetllama.com/api/user/auth/magic-link/request"
);

const headers = {
    "Host": "demo.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "jane@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "If an account exists for that email, a sign-in link has been sent.",
    "data": null
}
 

Example response (404):


{
    "success": false,
    "message": "Tenant could not be resolved for this host."
}
 

Example response (429):


{
    "message": "Too Many Attempts."
}
 

Consumes a valid, single-use, tenant-bound magic-link token and returns an EndUser-scoped JWT. Tokens are stored hashed, expire after 15 minutes, and cannot be reused.

Use Host: demo.vetllama.test in local development when testing against seeded tenant data.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/user/auth/magic-link/verify" \
    --header "Host: demo.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jane@example.com\",
    \"token\": \"2dc9LW5MsHGpJmw9v72Uw0HppFWlNqHNrHuKxnjIuRtb2UZ2DFbpURkiN0Lr7qkg\",
    \"device_token\": \"web-user-device\"
}"
const url = new URL(
    "https://api.vetllama.com/api/user/auth/magic-link/verify"
);

const headers = {
    "Host": "demo.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "jane@example.com",
    "token": "2dc9LW5MsHGpJmw9v72Uw0HppFWlNqHNrHuKxnjIuRtb2UZ2DFbpURkiN0Lr7qkg",
    "device_token": "web-user-device"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Logged in successfully.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "end_user",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "tenant_id": 1,
            "name": "Jane Customer",
            "email": "jane@example.com",
            "phone": null,
            "is_active": true
        }
    }
}
 

Example response (422):


{
    "success": false,
    "message": "The given data was invalid.",
    "errors": {
        "token": [
            "This magic link is invalid or has expired."
        ]
    }
}
 

Logout

requires authentication

Invalidates the current EndUser JWT and closes the latest open auth activity session. Tenant host resolution is still required.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/user/auth/logout" \
    --header "Authorization: Bearer {END_USER_JWT}" \
    --header "Host: demo.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/user/auth/logout"
);

const headers = {
    "Authorization": "Bearer {END_USER_JWT}",
    "Host": "demo.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Logged out successfully.",
    "data": null
}
 

Example response (403):


{
    "success": false,
    "message": "Unauthorized for this tenant."
}
 

Request      

POST api/user/auth/logout

Headers

Authorization        

Example: Bearer {END_USER_JWT}

Host        

Example: demo.vetllama.test

Content-Type        

Example: application/json

Accept        

Example: application/json

Refresh Token

requires authentication

Exchanges a valid EndUser JWT for a fresh EndUser JWT. The token must belong to the tenant resolved from the Host header.

Example request:
curl --request POST \
    "https://api.vetllama.com/api/user/auth/refresh" \
    --header "Authorization: Bearer {END_USER_JWT}" \
    --header "Host: demo.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/user/auth/refresh"
);

const headers = {
    "Authorization": "Bearer {END_USER_JWT}",
    "Host": "demo.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Token refreshed successfully.",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "token_type": "bearer",
        "guard": "end_user",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "tenant_id": 1,
            "email": "jane@example.com"
        }
    }
}
 

Request      

POST api/user/auth/refresh

Headers

Authorization        

Example: Bearer {END_USER_JWT}

Host        

Example: demo.vetllama.test

Content-Type        

Example: application/json

Accept        

Example: application/json

Current Profile

requires authentication

Returns the authenticated EndUser profile. The token must belong to the tenant resolved from the Host header.

Example request:
curl --request GET \
    --get "https://api.vetllama.com/api/user/auth/me" \
    --header "Authorization: Bearer {END_USER_JWT}" \
    --header "Host: demo.vetllama.test" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.vetllama.com/api/user/auth/me"
);

const headers = {
    "Authorization": "Bearer {END_USER_JWT}",
    "Host": "demo.vetllama.test",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "success": true,
    "message": "Profile fetched successfully.",
    "data": {
        "id": 1,
        "tenant_id": 1,
        "name": "Jane Customer",
        "email": "jane@example.com",
        "phone": null,
        "is_active": true
    }
}
 

Request      

GET api/user/auth/me

Headers

Authorization        

Example: Bearer {END_USER_JWT}

Host        

Example: demo.vetllama.test

Content-Type        

Example: application/json

Accept        

Example: application/json