{
    "openapi": "3.1.0",
    "info": {
        "title": "ZivoBooks API",
        "version": "1.2.0",
        "description": "Read a business’s customers, invoices, quotes, payments and products, and create customers and draft invoices. Sign in at POST /api/v1/auth/token, then send the session token and key ID on each call. See https://www.zivobooks.com.suncloudsystems.co.za/developers/docs for the guide.",
        "contact": {
            "email": null,
            "url": "https://www.zivobooks.com.suncloudsystems.co.za/developers"
        }
    },
    "servers": [
        {
            "url": "https://zivobooks.com"
        }
    ],
    "components": {
        "securitySchemes": {
            "sessionToken": {
                "type": "http",
                "scheme": "bearer",
                "description": "A session token (zbat_…) from POST /api/v1/auth/token, valid for one hour."
            },
            "keyId": {
                "type": "apiKey",
                "in": "header",
                "name": "X-Zivobooks-Key",
                "description": "The key ID (zbk_…) the session was issued for."
            },
            "basicAuth": {
                "type": "http",
                "scheme": "basic",
                "description": "Sign-in only: the key ID as the username and the secret as the password."
            }
        }
    },
    "security": [
        {
            "sessionToken": [],
            "keyId": []
        }
    ],
    "paths": {
        "/api/v1/auth/token": {
            "post": {
                "operationId": "auth.token",
                "tags": [
                    "Authentication"
                ],
                "summary": "Sign in",
                "description": "Exchanges a key ID and secret for a session token that lasts one hour. Send the credentials in the JSON body (or as HTTP Basic, key ID as the username). Limited to 10 attempts a minute per IP address.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "key_id",
                                    "secret"
                                ],
                                "properties": {
                                    "key_id": {
                                        "type": "string",
                                        "description": "The key ID, starting zbk_live_ or zbk_test_. Not secret."
                                    },
                                    "secret": {
                                        "type": "string",
                                        "description": "The secret, starting zb_live_ or zb_test_. Shown once when the key was created."
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    [],
                    {
                        "basicAuth": []
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "example": {
                                    "access_token": "zbat_8sJ2…",
                                    "token_type": "Bearer",
                                    "expires_in": 3600,
                                    "expires_at": "2026-10-03T11:00:00+02:00",
                                    "mode": "live",
                                    "key_id": "zbk_live_3k9xq…",
                                    "scopes": [
                                        "invoices.read"
                                    ],
                                    "business": {
                                        "id": 12,
                                        "name": "Moyo Hardware"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "The request is invalid: see the errors object for each field. Also returned when an Idempotency-Key is reused with a different body."
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            },
            "delete": {
                "operationId": "auth.destroy",
                "tags": [
                    "Authentication"
                ],
                "summary": "Sign out",
                "description": "Ends the current session token straight away. Optional: tokens expire on their own after an hour.",
                "security": [
                    {
                        "sessionToken": [],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/ping": {
            "get": {
                "operationId": "ping",
                "tags": [
                    "Authentication"
                ],
                "summary": "Check a session",
                "description": "Confirms the session works, whether it is test or live, which business it belongs to and when it expires. Needs no scope.",
                "security": [
                    {
                        "sessionToken": [],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true,
                                    "mode": "live",
                                    "business": {
                                        "id": 12,
                                        "name": "Moyo Hardware"
                                    },
                                    "session_expires_at": "2026-10-03T11:00:00+02:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/customers": {
            "get": {
                "operationId": "customers.list",
                "tags": [
                    "Customers"
                ],
                "summary": "List customers",
                "description": "Customers in ID order, oldest first. Requires the `customers.read` scope.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number, starting at 1.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "description": "Results per page, 1 to 100. Default 25.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "Only records changed at or after this ISO 8601 date or timestamp. Use it to sync incrementally.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "active or archived.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "required": false,
                        "description": "Part of the customer’s name.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "customers.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 42,
                                            "code": "C0042",
                                            "name": "Chikomo Builders",
                                            "email": "accounts@chikomo.co.zw",
                                            "phone": "+263 77 123 4567",
                                            "tax_number": "2000123456",
                                            "address": {
                                                "line1": "14 Samora Machel Ave",
                                                "line2": null,
                                                "city": "Harare",
                                                "region": "Harare",
                                                "postal_code": null
                                            },
                                            "status": "active",
                                            "created_at": "2026-09-14T08:12:00+02:00",
                                            "updated_at": "2026-10-01T15:40:22+02:00"
                                        }
                                    ],
                                    "links": {
                                        "first": "…?page=1",
                                        "last": "…?page=4",
                                        "prev": null,
                                        "next": "…?page=2"
                                    },
                                    "meta": {
                                        "current_page": 1,
                                        "from": 1,
                                        "last_page": 4,
                                        "per_page": 25,
                                        "to": 25,
                                        "total": 87
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            },
            "post": {
                "operationId": "customers.create",
                "tags": [
                    "Customers"
                ],
                "summary": "Create a customer",
                "description": "Adds a customer; the business’s next customer code is assigned. Send an Idempotency-Key header so a retry never creates a duplicate. Requires the `customers.write` scope.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "name"
                                ],
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "The customer’s name."
                                    },
                                    "email": {
                                        "type": "string",
                                        "description": "Where invoices are emailed."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "description": "Phone number, ideally with country code."
                                    },
                                    "tax_number": {
                                        "type": "string",
                                        "description": "Tax or VAT number."
                                    },
                                    "address": {
                                        "type": "object",
                                        "description": "line1, line2, city, region and postal_code, all optional."
                                    },
                                    "notes": {
                                        "type": "string",
                                        "description": "Internal notes, up to 2,000 characters."
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "sessionToken": [
                            "customers.write"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 42,
                                        "code": "C0042",
                                        "name": "Chikomo Builders",
                                        "email": "accounts@chikomo.co.zw",
                                        "phone": "+263 77 123 4567",
                                        "tax_number": "2000123456",
                                        "address": {
                                            "line1": "14 Samora Machel Ave",
                                            "line2": null,
                                            "city": "Harare",
                                            "region": "Harare",
                                            "postal_code": null
                                        },
                                        "status": "active",
                                        "created_at": "2026-09-14T08:12:00+02:00",
                                        "updated_at": "2026-10-01T15:40:22+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "The request is invalid: see the errors object for each field. Also returned when an Idempotency-Key is reused with a different body."
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/customers/{id}": {
            "get": {
                "operationId": "customers.show",
                "tags": [
                    "Customers"
                ],
                "summary": "Get a customer",
                "description": "One customer by ID. Requires the `customers.read` scope.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The customer’s ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "customers.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 42,
                                        "code": "C0042",
                                        "name": "Chikomo Builders",
                                        "email": "accounts@chikomo.co.zw",
                                        "phone": "+263 77 123 4567",
                                        "tax_number": "2000123456",
                                        "address": {
                                            "line1": "14 Samora Machel Ave",
                                            "line2": null,
                                            "city": "Harare",
                                            "region": "Harare",
                                            "postal_code": null
                                        },
                                        "status": "active",
                                        "created_at": "2026-09-14T08:12:00+02:00",
                                        "updated_at": "2026-10-01T15:40:22+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "404": {
                        "description": "No record with that ID in this business."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/invoices": {
            "get": {
                "operationId": "invoices.list",
                "tags": [
                    "Invoices"
                ],
                "summary": "List invoices",
                "description": "Invoices in ID order, without their lines. Amounts are strings in the invoice’s own currency, so no precision is lost. Requires the `invoices.read` scope.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number, starting at 1.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "description": "Results per page, 1 to 100. Default 25.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "Only records changed at or after this ISO 8601 date or timestamp. Use it to sync incrementally.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "draft, approved, issued, sent, viewed, part_paid, paid, overdue or void.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "customer_id",
                        "in": "query",
                        "required": false,
                        "description": "Only this customer’s invoices.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "sales_channel",
                        "in": "query",
                        "required": false,
                        "description": "pos for sales rung up on the till.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "invoices.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 1290,
                                            "number": "INV-0142",
                                            "status": "paid",
                                            "customer": {
                                                "id": 42,
                                                "name": "Chikomo Builders"
                                            },
                                            "issue_date": "2026-09-20",
                                            "due_date": "2026-10-04",
                                            "currency": "USD",
                                            "subtotal": "1113.04",
                                            "tax_total": "166.96",
                                            "total": "1280.00",
                                            "balance": "0.00",
                                            "sales_channel": null,
                                            "portal_url": "https://app.zivobooks.com/invoices/9fK2…",
                                            "created_at": "2026-09-20T09:01:00+02:00",
                                            "updated_at": "2026-10-02T11:25:10+02:00"
                                        }
                                    ],
                                    "links": {
                                        "first": "…?page=1",
                                        "last": "…?page=4",
                                        "prev": null,
                                        "next": "…?page=2"
                                    },
                                    "meta": {
                                        "current_page": 1,
                                        "from": 1,
                                        "last_page": 4,
                                        "per_page": 25,
                                        "to": 25,
                                        "total": 87
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            },
            "post": {
                "operationId": "invoices.create",
                "tags": [
                    "Invoices"
                ],
                "summary": "Create a draft invoice",
                "description": "Creates a draft invoice in the business’s base currency, numbered in its own sequence. Lines that name a product take its description, price and tax category unless you send them. Someone in the business reviews and sends the draft. Send an Idempotency-Key header. Requires the `invoices.write` scope.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "customer_id",
                                    "items"
                                ],
                                "properties": {
                                    "customer_id": {
                                        "type": "integer",
                                        "description": "One of the business’s customers."
                                    },
                                    "issue_date": {
                                        "type": "string",
                                        "description": "YYYY-MM-DD. Defaults to today."
                                    },
                                    "due_date": {
                                        "type": "string",
                                        "description": "YYYY-MM-DD, on or after issue_date. Defaults to 14 days after it."
                                    },
                                    "items": {
                                        "type": "array",
                                        "description": "1 to 200 lines, each with quantity and either product_id or description + unit_price; tax_category_id is optional."
                                    },
                                    "notes": {
                                        "type": "string",
                                        "description": "Shown on the invoice."
                                    },
                                    "terms": {
                                        "type": "string",
                                        "description": "Payment terms shown on the invoice."
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "sessionToken": [
                            "invoices.write"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 1290,
                                        "number": "INV-0142",
                                        "status": "draft",
                                        "customer": {
                                            "id": 42,
                                            "name": "Chikomo Builders"
                                        },
                                        "issue_date": "2026-09-20",
                                        "due_date": "2026-10-04",
                                        "currency": "USD",
                                        "subtotal": "1113.04",
                                        "tax_total": "166.96",
                                        "total": "1280.00",
                                        "balance": "1280.00",
                                        "sales_channel": null,
                                        "items": [
                                            {
                                                "description": "Roof repair",
                                                "quantity": "1.000000",
                                                "unit_price": "1113.04",
                                                "tax_amount": "166.96",
                                                "line_total": "1280.00",
                                                "product_id": 7
                                            }
                                        ],
                                        "portal_url": "https://app.zivobooks.com/invoices/9fK2…",
                                        "created_at": "2026-09-20T09:01:00+02:00",
                                        "updated_at": "2026-10-02T11:25:10+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "The request is invalid: see the errors object for each field. Also returned when an Idempotency-Key is reused with a different body."
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/invoices/{id}": {
            "get": {
                "operationId": "invoices.show",
                "tags": [
                    "Invoices"
                ],
                "summary": "Get an invoice",
                "description": "One invoice with its lines and the link the customer uses to view and pay it. Requires the `invoices.read` scope.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The invoice’s ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "invoices.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 1290,
                                        "number": "INV-0142",
                                        "status": "paid",
                                        "customer": {
                                            "id": 42,
                                            "name": "Chikomo Builders"
                                        },
                                        "issue_date": "2026-09-20",
                                        "due_date": "2026-10-04",
                                        "currency": "USD",
                                        "subtotal": "1113.04",
                                        "tax_total": "166.96",
                                        "total": "1280.00",
                                        "balance": "0.00",
                                        "sales_channel": null,
                                        "items": [
                                            {
                                                "description": "Roof repair",
                                                "quantity": "1.000000",
                                                "unit_price": "1113.04",
                                                "tax_amount": "166.96",
                                                "line_total": "1280.00",
                                                "product_id": 7
                                            }
                                        ],
                                        "portal_url": "https://app.zivobooks.com/invoices/9fK2…",
                                        "created_at": "2026-09-20T09:01:00+02:00",
                                        "updated_at": "2026-10-02T11:25:10+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "404": {
                        "description": "No record with that ID in this business."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/quotes": {
            "get": {
                "operationId": "quotes.list",
                "tags": [
                    "Quotes"
                ],
                "summary": "List quotes",
                "description": "Quotes in ID order. Requires the `quotes.read` scope.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number, starting at 1.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "description": "Results per page, 1 to 100. Default 25.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "Only records changed at or after this ISO 8601 date or timestamp. Use it to sync incrementally.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "draft, sent, viewed, accepted, declined, expired, converted or cancelled.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "quotes.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 311,
                                            "number": "QUO-0057",
                                            "status": "accepted",
                                            "customer": {
                                                "id": 42,
                                                "name": "Chikomo Builders"
                                            },
                                            "issue_date": "2026-09-10",
                                            "valid_until": "2026-10-10",
                                            "total": "1280.00",
                                            "accepted_at": "2026-09-12T10:03:00+02:00",
                                            "updated_at": "2026-09-12T10:03:00+02:00"
                                        }
                                    ],
                                    "links": {
                                        "first": "…?page=1",
                                        "last": "…?page=4",
                                        "prev": null,
                                        "next": "…?page=2"
                                    },
                                    "meta": {
                                        "current_page": 1,
                                        "from": 1,
                                        "last_page": 4,
                                        "per_page": 25,
                                        "to": 25,
                                        "total": 87
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/quotes/{id}": {
            "get": {
                "operationId": "quotes.show",
                "tags": [
                    "Quotes"
                ],
                "summary": "Get a quote",
                "description": "One quote by ID. Requires the `quotes.read` scope.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The quote’s ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "quotes.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 311,
                                        "number": "QUO-0057",
                                        "status": "accepted",
                                        "customer": {
                                            "id": 42,
                                            "name": "Chikomo Builders"
                                        },
                                        "issue_date": "2026-09-10",
                                        "valid_until": "2026-10-10",
                                        "total": "1280.00",
                                        "accepted_at": "2026-09-12T10:03:00+02:00",
                                        "updated_at": "2026-09-12T10:03:00+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "404": {
                        "description": "No record with that ID in this business."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/payments": {
            "get": {
                "operationId": "payments.list",
                "tags": [
                    "Payments"
                ],
                "summary": "List payments",
                "description": "Customer payments in ID order, with the invoices each one paid. Requires the `payments.read` scope.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number, starting at 1.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "description": "Results per page, 1 to 100. Default 25.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "Only records changed at or after this ISO 8601 date or timestamp. Use it to sync incrementally.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "customer_id",
                        "in": "query",
                        "required": false,
                        "description": "Only this customer’s payments.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "payments.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 988,
                                            "customer": {
                                                "id": 42,
                                                "name": "Chikomo Builders"
                                            },
                                            "amount": "1280.00",
                                            "currency": "USD",
                                            "payment_date": "2026-10-02",
                                            "method": "mobile_money",
                                            "reference": "EC-77812",
                                            "allocations": [
                                                {
                                                    "invoice_id": 1290,
                                                    "invoice_number": "INV-0142",
                                                    "amount": "1280.00"
                                                }
                                            ],
                                            "created_at": "2026-10-02T11:25:09+02:00"
                                        }
                                    ],
                                    "links": {
                                        "first": "…?page=1",
                                        "last": "…?page=4",
                                        "prev": null,
                                        "next": "…?page=2"
                                    },
                                    "meta": {
                                        "current_page": 1,
                                        "from": 1,
                                        "last_page": 4,
                                        "per_page": 25,
                                        "to": 25,
                                        "total": 87
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/payments/{id}": {
            "get": {
                "operationId": "payments.show",
                "tags": [
                    "Payments"
                ],
                "summary": "Get a payment",
                "description": "One payment by ID. Requires the `payments.read` scope.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The payment’s ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "payments.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 988,
                                        "customer": {
                                            "id": 42,
                                            "name": "Chikomo Builders"
                                        },
                                        "amount": "1280.00",
                                        "currency": "USD",
                                        "payment_date": "2026-10-02",
                                        "method": "mobile_money",
                                        "reference": "EC-77812",
                                        "allocations": [
                                            {
                                                "invoice_id": 1290,
                                                "invoice_number": "INV-0142",
                                                "amount": "1280.00"
                                            }
                                        ],
                                        "created_at": "2026-10-02T11:25:09+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "404": {
                        "description": "No record with that ID in this business."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/products": {
            "get": {
                "operationId": "products.list",
                "tags": [
                    "Products"
                ],
                "summary": "List products",
                "description": "Products and services in ID order. Tracked products include a stock object with the quantity on hand in total and per branch; services and untracked products have stock: null. A stock movement counts as a change, so updated_since picks up stock changes. Requires the `products.read` scope.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number, starting at 1.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "description": "Results per page, 1 to 100. Default 25.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "Only records changed at or after this ISO 8601 date or timestamp. Use it to sync incrementally.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "active",
                        "in": "query",
                        "required": false,
                        "description": "true for products still sold, false for archived ones.",
                        "schema": {
                            "type": "boolean"
                        }
                    },
                    {
                        "name": "barcode",
                        "in": "query",
                        "required": false,
                        "description": "Exact barcode, for looking up a scanned item.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "sku",
                        "in": "query",
                        "required": false,
                        "description": "Exact SKU or product code.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "track_stock",
                        "in": "query",
                        "required": false,
                        "description": "true for products whose stock is tracked.",
                        "schema": {
                            "type": "boolean"
                        }
                    },
                    {
                        "name": "low_stock",
                        "in": "query",
                        "required": false,
                        "description": "true for tracked products at or below their reorder level.",
                        "schema": {
                            "type": "boolean"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "products.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 7,
                                            "name": "Cement 50kg",
                                            "sku": "CEM-50",
                                            "barcode": "6009880000017",
                                            "type": "product",
                                            "description": "Portland cement, 50kg bag.",
                                            "default_price": "12.50",
                                            "is_active": true,
                                            "track_stock": true,
                                            "stock": {
                                                "on_hand": "36.000000",
                                                "reorder_level": "40.000000",
                                                "low": true,
                                                "branches": [
                                                    {
                                                        "branch_id": 1,
                                                        "branch": "Head office",
                                                        "on_hand": "24.000000"
                                                    },
                                                    {
                                                        "branch_id": 2,
                                                        "branch": "Bulawayo",
                                                        "on_hand": "12.000000"
                                                    }
                                                ]
                                            },
                                            "updated_at": "2026-10-04T09:12:44+02:00"
                                        }
                                    ],
                                    "links": {
                                        "first": "…?page=1",
                                        "last": "…?page=4",
                                        "prev": null,
                                        "next": "…?page=2"
                                    },
                                    "meta": {
                                        "current_page": 1,
                                        "from": 1,
                                        "last_page": 4,
                                        "per_page": 25,
                                        "to": 25,
                                        "total": 87
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        },
        "/api/v1/products/{id}": {
            "get": {
                "operationId": "products.show",
                "tags": [
                    "Products"
                ],
                "summary": "Get a product",
                "description": "One product or service by ID, with stock on hand per branch when it’s tracked. Requires the `products.read` scope.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The product’s ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "security": [
                    {
                        "sessionToken": [
                            "products.read"
                        ],
                        "keyId": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 7,
                                        "name": "Cement 50kg",
                                        "sku": "CEM-50",
                                        "barcode": "6009880000017",
                                        "type": "product",
                                        "description": "Portland cement, 50kg bag.",
                                        "default_price": "12.50",
                                        "is_active": true,
                                        "track_stock": true,
                                        "stock": {
                                            "on_hand": "36.000000",
                                            "reorder_level": "40.000000",
                                            "low": true,
                                            "branches": [
                                                {
                                                    "branch_id": 1,
                                                    "branch": "Head office",
                                                    "on_hand": "24.000000"
                                                },
                                                {
                                                    "branch_id": 2,
                                                    "branch": "Bulawayo",
                                                    "on_hand": "12.000000"
                                                }
                                            ]
                                        },
                                        "updated_at": "2026-10-04T09:12:44+02:00"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Wrong key ID or secret, or the session token is missing, expired or ended, or the X-Zivobooks-Key header doesn’t match it. Sign in again."
                    },
                    "403": {
                        "description": "Signed in but not allowed this: the key lacks the scope, the business suspended the key set, the developer account is suspended, or the business’s plan doesn’t include the API."
                    },
                    "404": {
                        "description": "No record with that ID in this business."
                    },
                    "429": {
                        "description": "More than 120 calls a minute from one key, or more than 10 sign-ins a minute from one IP. Wait for the seconds in the Retry-After header."
                    }
                }
            }
        }
    },
    "x-scopes": {
        "customers.read": {
            "label": "Customers",
            "access": "read",
            "description": "Names, contact details, tax numbers and addresses of the business’s customers."
        },
        "invoices.read": {
            "label": "Invoices",
            "access": "read",
            "description": "Invoices with their lines, totals, balances, status and customer link."
        },
        "quotes.read": {
            "label": "Quotes",
            "access": "read",
            "description": "Quotes with totals, status and validity."
        },
        "payments.read": {
            "label": "Payments",
            "access": "read",
            "description": "Customer payments and the invoices they were allocated to."
        },
        "products.read": {
            "label": "Products",
            "access": "read",
            "description": "Products and services with prices, barcodes and stock on hand per branch."
        },
        "customers.write": {
            "label": "Create customers",
            "access": "write",
            "description": "Add new customers."
        },
        "invoices.write": {
            "label": "Create draft invoices",
            "access": "write",
            "description": "Create invoices as drafts. Someone in the business still reviews and sends them."
        }
    },
    "x-webhooks": {
        "invoice.sent": {
            "description": "An invoice was sent to the customer by email or WhatsApp.",
            "payloadKey": "invoice",
            "scope": "invoices.read"
        },
        "invoice.paid": {
            "description": "An invoice was paid in full.",
            "payloadKey": "invoice",
            "scope": "invoices.read"
        },
        "payment.recorded": {
            "description": "A customer payment was recorded, from any source.",
            "payloadKey": "payment",
            "scope": "payments.read"
        },
        "quote.accepted": {
            "description": "A customer accepted a quote online.",
            "payloadKey": "quote",
            "scope": "quotes.read"
        },
        "stock.low": {
            "description": "A tracked product’s total stock dropped to or below its reorder level. Sent once when it crosses, not on every sale after.",
            "payloadKey": "product",
            "scope": "products.read"
        }
    }
}