{"openapi":"3.1.0","info":{"title":"Retavon API","version":"0.1.0","description":"Business software for specialty retailers. Built to be driven by AI agents as well as people: every operation and field is described, errors say how to recover, and lists page by cursor."},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key issued to a tenant, prefixed rtv_."}},"schemas":{"CreateMessageTemplateInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The template's name, unique within the tenant."},"channel":{"type":"string","enum":["email","sms"],"description":"email or sms."},"purpose":{"type":"string","enum":["service","marketing"],"description":"service: about the customer's own orders, deliveries and service. marketing: promotional. It decides which consent applies."},"subject":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The subject line, for email only."},{"type":"null"}],"description":"The subject line, for email only."},"body":{"type":"string","minLength":1,"maxLength":5000,"description":"The message text. Placeholders are written {{thing.field}} and filled in when the message is queued. Placeholders are {{thing.field}}: {{customer.given_name}} (the customer's name when they have no given name), {{customer.name}}, {{customer.family_name}}, {{customer.customer_number}}, the customer's address and {{business.name}} always, and for an automation the fields its event offers, listed in each automation's placeholders. An appointment event offers {{appointment.scheduled_window}} (the window in words at the visit's location, such as \"Thu Oct 15, 8:00 AM to 12:00 PM\"), {{appointment.eta_local}} and {{appointment.location_name}}; use these for times, since {{appointment.scheduled_start}} is in UTC. delivery.site_not_ready adds {{appointment.readiness_summary}} (what is still needed) and {{appointment.ready_by}}. Costs, internal notes and ids are never offered. An automation whose template uses a placeholder its event does not offer is refused; one with nothing to fill it is left blank."}},"required":["name","channel","purpose","body"],"additionalProperties":false,"examples":[{"name":"Delivery booked","channel":"sms","purpose":"service","body":"Hi {{customer.given_name}}, your delivery from {{business.name}} is booked for {{appointment.scheduled_window}}."}]},"UpdateMessageTemplateInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The template's name, unique within the tenant."},"channel":{"type":"string","enum":["email","sms"],"description":"email or sms."},"purpose":{"type":"string","enum":["service","marketing"],"description":"service: about the customer's own orders, deliveries and service. marketing: promotional. It decides which consent applies."},"subject":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The subject line, for email only."},{"type":"null"}],"description":"The subject line, for email only."},"body":{"type":"string","minLength":1,"maxLength":5000,"description":"The message text. Placeholders are written {{thing.field}} and filled in when the message is queued. Placeholders are {{thing.field}}: {{customer.given_name}} (the customer's name when they have no given name), {{customer.name}}, {{customer.family_name}}, {{customer.customer_number}}, the customer's address and {{business.name}} always, and for an automation the fields its event offers, listed in each automation's placeholders. An appointment event offers {{appointment.scheduled_window}} (the window in words at the visit's location, such as \"Thu Oct 15, 8:00 AM to 12:00 PM\"), {{appointment.eta_local}} and {{appointment.location_name}}; use these for times, since {{appointment.scheduled_start}} is in UTC. delivery.site_not_ready adds {{appointment.readiness_summary}} (what is still needed) and {{appointment.ready_by}}. Costs, internal notes and ids are never offered. An automation whose template uses a placeholder its event does not offer is refused; one with nothing to fill it is left blank."},"is_active":{"type":"boolean","description":"False once the template is retired."}},"additionalProperties":false,"examples":[{"body":"Hi {{customer.given_name}}, see you {{appointment.scheduled_window}}."},{"is_active":false}]},"CreateAutomationInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The automation's name, unique within the tenant."},"event_type":{"type":"string","enum":["customer.created","order.opened","order.completed","order.cancelled","sale.fulfilled","sale.returned","payment.received","payment.refunded","payment.reversed","appointment.scheduled","appointment.rescheduled","appointment.completed","appointment.cancelled","work_order.opened","work_order.completed","water_test.recorded","appointment.en_route","appointment.arrived","appointment.failed","appointment.upcoming","delivery.site_not_ready","site_check.recorded","account_payment.received","account_payment.refunded","account_payment.reversed","receipt.issued","statement.issued","work_order.signed_off","lead.created","lead.won","lead.lost"],"description":"The event that sets it off: one of the types listed on event.event_type that is about a customer, so any but stock.received."},"message_template_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The message to send."},"delay_minutes":{"default":0,"type":"integer","minimum":0,"maximum":525600,"description":"How long after the event to send it. 0 sends at once."},"appointment_kind":{"anyOf":[{"type":"string","enum":["delivery","service","consultation"]},{"type":"null"}],"description":"For an appointment event: answer only visits of this kind (a delivery confirmation must not go to a service call). NULL: every kind."}},"required":["name","event_type","message_template_id"],"additionalProperties":false,"examples":[{"name":"Text the delivery confirmation","event_type":"appointment.scheduled","message_template_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"name":"Text that the crew is on the way","event_type":"appointment.en_route","message_template_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","appointment_kind":"delivery"},{"name":"Care reminder after 30 days","event_type":"sale.fulfilled","message_template_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","delay_minutes":43200}]},"UpdateAutomationInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The automation's name, unique within the tenant."},"event_type":{"type":"string","enum":["customer.created","order.opened","order.completed","order.cancelled","sale.fulfilled","sale.returned","payment.received","payment.refunded","payment.reversed","appointment.scheduled","appointment.rescheduled","appointment.completed","appointment.cancelled","work_order.opened","work_order.completed","water_test.recorded","appointment.en_route","appointment.arrived","appointment.failed","appointment.upcoming","delivery.site_not_ready","site_check.recorded","account_payment.received","account_payment.refunded","account_payment.reversed","receipt.issued","statement.issued","work_order.signed_off","lead.created","lead.won","lead.lost"],"description":"The event that sets it off: one of the types listed on event.event_type that is about a customer, so any but stock.received."},"message_template_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The message to send."},"delay_minutes":{"type":"integer","minimum":0,"maximum":525600,"description":"How long after the event to send it. 0 sends at once."},"is_active":{"type":"boolean","description":"False while the automation is switched off."},"appointment_kind":{"anyOf":[{"type":"string","enum":["delivery","service","consultation"]},{"type":"null"}],"description":"For an appointment event: answer only visits of this kind (a delivery confirmation must not go to a service call). NULL: every kind."}},"additionalProperties":false,"examples":[{"is_active":false},{"delay_minutes":60}]},"SendMessageInput":{"type":"object","properties":{"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"contact_point_id":{"description":"The address or number it goes to. Its kind must match the channel, and its consent decides whether it goes at all.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channel":{"description":"email or sms. Taken from the template when one is named; required without one.","type":"string","enum":["email","sms"]},"purpose":{"description":"service or marketing. A marketing message goes only where marketing consent is granted. A service message goes unless service consent is denied. A message written from a template has the template's purpose and channel. Fixed once the message is written. Taken from the template when one is named; required without one.","type":"string","enum":["service","marketing"]},"message_template_id":{"description":"Write the message from this template. Otherwise send body, and subject for email.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"subject":{"type":"string","minLength":1,"maxLength":200,"description":"The subject line, for email."},"body":{"type":"string","minLength":1,"maxLength":5000,"description":"The message as it will be or was sent, with the details filled in. Placeholders are {{thing.field}}: {{customer.given_name}} (the customer's name when they have no given name), {{customer.name}}, {{customer.family_name}}, {{customer.customer_number}}, the customer's address and {{business.name}} always, and for an automation the fields its event offers, listed in each automation's placeholders. An appointment event offers {{appointment.scheduled_window}} (the window in words at the visit's location, such as \"Thu Oct 15, 8:00 AM to 12:00 PM\"), {{appointment.eta_local}} and {{appointment.location_name}}; use these for times, since {{appointment.scheduled_start}} is in UTC. delivery.site_not_ready adds {{appointment.readiness_summary}} (what is still needed) and {{appointment.ready_by}}. Costs, internal notes and ids are never offered. An automation whose template uses a placeholder its event does not offer is refused; one with nothing to fill it is left blank."},"send_after":{"description":"The earliest time to send it. Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"appointment_id":{"description":"The visit the message is about, when it is about one: named by whoever writes it, or taken from the event an automation answers. While it is queued, moving the visit or calling it off withdraws it (suppressed, with the reason), so the customer is never told a time that no longer holds. Fixed once written. Name it when the message gives a visit's time, so a later move or cancellation withdraws it if it has not gone.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key writes and sends nothing and answers with the message the first one wrote, so a retry after a slow answer never messages the customer twice. Use a new key for each new message.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"required":["customer_id"],"additionalProperties":false,"examples":[{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","channel":"sms","purpose":"service","body":"Hi {{customer.given_name}}, your filter is in. Pick it up at the Boise showroom any time."},{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","message_template_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"UpdateMessageInput":{"type":"object","properties":{"status":{"type":"string","enum":["sent","failed","suppressed"],"description":"queued: waiting for send_after. sending: handed to the email or text service, its answer not yet recorded; set and cleared only by the system. sent: accepted by the provider. failed: the provider refused it, or never confirmed it; status_reason says why. suppressed: not sent, because consent does not allow it, the address is dead, or it was withdrawn (held back by hand, or its visit moved or was called off while it waited); set by the system when the message is queued or taken to send, and by an outside sender if marking it sent is refused because consent was withdrawn meanwhile. Sent, failed and suppressed are final."},"status_reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the message failed or was suppressed."},"provider_reference":{"type":"string","minLength":1,"maxLength":200,"description":"The provider's id for the message, for tracing delivery."}},"required":["status"],"additionalProperties":false,"examples":[{"status":"sent","provider_reference":"SM8f3c"},{"status":"failed","status_reason":"Carrier rejected the number."}]},"UpdateCustomerReplyInput":{"type":"object","properties":{"handled":{"type":"boolean","const":true,"description":"Send true once someone has dealt with it: answered, called back, or nothing needed."}},"required":["handled"],"additionalProperties":false,"examples":[{"handled":true}]},"IssueReceiptInput":{"type":"object","properties":{"cash_tendered":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash the customer handed over, when they paid cash: at least the cash this receipt covers. Recorded only here, to print the change. NULL when no cash was tendered or it was not recorded. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who issued it, printed on it as \"Served by\". Leave it out for the API key's own staff member.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"additionalProperties":false,"examples":[{},{"cash_tendered":"100.00"}]},"PrintReceiptInput":{"type":"object","properties":{"gift_receipt":{"default":false,"description":"A gift receipt: the goods without their prices, tenders or totals, for the person the goods are for. Printed only. False (the default): the full receipt.","type":"boolean"},"format":{"default":"text","description":"text (the default) or html.","type":"string","enum":["text","html"]},"staff_member_id":{"description":"Who printed or sent it. Leave it out for the API key's own staff member.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"additionalProperties":false,"examples":[{},{"gift_receipt":true},{"format":"html"}]},"EmailReceiptInput":{"type":"object","properties":{"contact_point_id":{"description":"The email address to send it to: one of the receipt's customer's contact points (customer_id). Leave it out for their primary email address. A receipt with no customer cannot be emailed until its order names one.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"staff_member_id":{"description":"Who printed or sent it. Leave it out for the API key's own staff member.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key writes and sends nothing and answers with the message the first one wrote, so a retry after a slow answer never messages the customer twice. Use a new key for each new message.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"additionalProperties":false,"examples":[{},{"contact_point_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"IssueStatementInput":{"type":"object","properties":{"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day the statement covers. It must be over at every location of the business, and later than the customer's previous statement. For a month's statements, its last day, for example \"2026-09-30\"."},"period_start":{"description":"The first day the statement covers: the day after the customer's previous statement ended. For a customer's first statement, the month that ends on period_end (the first of its month when period_end is a month's last day, else the day after the same date a month before) unless another day is named. Send it only for a customer's first statement.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who issued it. Leave it out for the API key's own staff member.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["period_end"],"additionalProperties":false,"examples":[{"period_end":"2026-09-30"}]},"IssueStatementsInput":{"type":"object","properties":{"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day the statement covers. It must be over at every location of the business, and later than the customer's previous statement. For a month's statements, its last day, for example \"2026-09-30\"."},"only_owing":{"default":true,"description":"True (the default): only customers who owe something at the end of the period and still owe as the statements are issued. False: also those with anything handed over, returned, paid or given back in the period, or a balance either way, and those who have paid since.","type":"boolean"},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."}},"required":["period_end"],"additionalProperties":false,"examples":[{"period_end":"2026-09-30"},{"period_end":"2026-09-30","only_owing":false}]},"EmailStatementInput":{"type":"object","properties":{"contact_point_id":{"description":"The email address to send it to: one of the customer's contact points. Leave it out for their primary email address.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key writes and sends nothing and answers with the message the first one wrote, so a retry after a slow answer never messages the customer twice. Use a new key for each new message.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"additionalProperties":false,"examples":[{},{"contact_point_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"TakeAccountPaymentInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much was received. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid: cash, a check, a card or another method of the business's own. Not store credit, a lender or On account. A reversal goes back by the method of the payment it reverses."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the money: a check number, a card processor's transaction id, a card's last four digits. Never a full card number: one is refused. A reversal carries the reference of the payment it reverses unless it is given its own."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example \"September statement\" or why the credit was refunded."},"apply":{"description":"oldest_first: apply it to the orders the customer owes on, the one due first first. none: keep it all as account credit. Defaults to the business's choice (getPolicy, account_payment_application). Leave it out when sending applications.","type":"string","enum":["oldest_first","none"]},"applications":{"description":"Apply it to these orders instead, each up to what it has left to pay: for example the invoices the customer's check names, or a deposit on an open order. What is not applied stays as account credit.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much of the credit to put on it. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["sales_order_id","amount"],"additionalProperties":false}},"staff_member_id":{"description":"Who took or gave back the money. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","payment_method_id","location_id","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"1000.00","payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","reference":"Check 4417","note":"September statement","idempotency_key":"0199a3c4-onaccount-4417"},{"amount":"500.00","payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","applications":[{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","amount":"500.00"}],"idempotency_key":"0199a3c4-onaccount-2007"}]},"ApplyAccountCreditInput":{"type":"object","properties":{"applications":{"description":"Apply it to these orders, each up to what it has left to pay. Leave it out to apply it to what the customer owes, the order due first first.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much of the credit to put on it. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["sales_order_id","amount"],"additionalProperties":false}},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Without applications: the most to apply. Leave it out to apply all the credit that is owed against. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who took or gave back the money. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"idempotency_key":"0199a3c4-apply-credit-1"},{"applications":[{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","amount":"300.00"}],"idempotency_key":"0199a3c4-apply-credit-2"}]},"RefundAccountCreditInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much to give back. At most the customer's account credit. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid: cash, a check, a card or another method of the business's own. Not store credit, a lender or On account. A reversal goes back by the method of the payment it reverses."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the money: a check number, a card processor's transaction id, a card's last four digits. Never a full card number: one is refused. A reversal carries the reference of the payment it reverses unless it is given its own."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example \"September statement\" or why the credit was refunded."},"staff_member_id":{"description":"Who took or gave back the money. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","payment_method_id","location_id","note","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"150.00","payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","reference":"Check 10233","note":"Overpaid the September statement; customer asked for it back","idempotency_key":"0199a3c4-credit-refund-1"}]},"ReverseAccountPaymentInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much did not arrive. Leave it out to reverse all that is left of it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example \"September statement\" or why the credit was refunded."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"note":"Check 4417 returned NSF","idempotency_key":"0199a3c4-nsf-4417"}]},"AssessFinanceChargesInput":{"type":"object","properties":{"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day of the month charged for. The month must be over at every location, and after any month the customer was charged for before. getFinanceChargesDue shows what it would charge."},"staff_member_id":{"description":"Who made the charge. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["period_end","idempotency_key"],"additionalProperties":false,"examples":[{"period_end":"2026-09-30","idempotency_key":"0199a3c4-finance-2026-09"}]},"WaiveFinanceChargeInput":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Why, for example \"paid late because our statement went to the old address\"."},"staff_member_id":{"description":"Who waived it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["reason","idempotency_key"],"additionalProperties":false,"examples":[{"reason":"Paid late because our September statement went to the old address","idempotency_key":"0199a3c4-waive-1003"}]},"SignInInput":{"type":"object","properties":{"business":{"type":"string","minLength":1,"maxLength":100,"description":"The business's slug: the short lowercase name it was set up under, for example snake-river-pool-and-spa."},"email":{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+$","description":"The person's work email address, as their staff record has it."},"password":{"type":"string","minLength":1,"maxLength":256,"description":"Their password."},"device_name":{"type":"string","minLength":1,"maxLength":100,"description":"What the person is signing in on, for example \"Front counter iPad\". Names the session in the list of keys, so it can be told apart and revoked."}},"required":["business","email","password"],"additionalProperties":false,"examples":[{"business":"snake-river-pool-and-spa","email":"pat@thespateam.com","password":"correct horse battery","device_name":"Front counter"}]},"SetPasswordInput":{"type":"object","properties":{"business":{"type":"string","minLength":1,"maxLength":100,"description":"The business's slug: the short lowercase name it was set up under, for example snake-river-pool-and-spa."},"email":{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+$","description":"The person's work email address, as their staff record has it."},"sign_in_code":{"type":"string","minLength":1,"maxLength":40,"description":"The one-time sign-in code a manager issued (issueSignInCode), for example ABCD-EFGH-JKMN. Case, spaces and hyphens do not matter."},"new_password":{"type":"string","minLength":12,"maxLength":256,"description":"The new password: at least 12 characters. Longer is stronger; a few unrelated words make a good one."},"device_name":{"type":"string","minLength":1,"maxLength":100,"description":"What the person is signing in on, for example \"Front counter iPad\". Names the session in the list of keys, so it can be told apart and revoked."}},"required":["business","email","sign_in_code","new_password"],"additionalProperties":false,"examples":[{"business":"snake-river-pool-and-spa","email":"pat@thespateam.com","sign_in_code":"ABCD-EFGH-JKMN","new_password":"correct horse battery"}]},"ChangePasswordInput":{"type":"object","properties":{"current_password":{"type":"string","minLength":1,"maxLength":256,"description":"The password the person signs in with now."},"new_password":{"type":"string","minLength":12,"maxLength":256,"description":"The new password: at least 12 characters. Longer is stronger; a few unrelated words make a good one."}},"required":["current_password","new_password"],"additionalProperties":false,"examples":[{"current_password":"correct horse battery","new_password":"staple purple lantern"}]},"IssueApiKeyInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Who or what holds the key, for example \"Claude Desktop, front counter\"."},"acts_for":{"type":"string","enum":["me","nobody"],"description":"me: the key acts for the person signed in, holds what they hold, and its work is theirs in the audit trail; it needs a session key and the person's password, and takes no roles. nobody: it acts for no person and holds only the roles given; it needs staff.manage for the whole business and every permission each role carries where it is given. A key that acts for nobody with no roles must be read_only."},"roles":{"default":[],"description":"For acts_for nobody: the roles the key holds, which decide what it may see and, unless it is read_only, do. Leave empty for acts_for me.","maxItems":20,"type":"array","items":{"type":"object","properties":{"role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A role the key will hold in its own right."},"location_id":{"default":null,"description":"The one location it holds the role at. Null for everywhere. Accepts the record's id or its branch code.","anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"x-natural-key":"branch_code"}},"required":["role_id"],"additionalProperties":false}},"read_only":{"default":false,"description":"true: the key may only look things up. Every request but a read is refused, whatever its roles or its person hold. Required for a key that acts for nobody and has no roles.","type":"boolean"},"expires_in_days":{"default":90,"description":"How many days, on the business's clock, until the key stops working: 1 to 365, 90 by default.","type":"integer","minimum":1,"maximum":365},"password":{"type":"string","minLength":1,"maxLength":256,"description":"For acts_for me: the person's password, entered again, so a session left open cannot be used to make a key that outlives it."}},"required":["name","acts_for"],"additionalProperties":false,"examples":[{"name":"Claude Desktop, front counter","acts_for":"me","password":"correct horse battery"},{"name":"Nightly stock sync","acts_for":"nobody","roles":[{"role_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","location_id":null}],"expires_in_days":365},{"name":"Owner's reporting assistant","acts_for":"nobody","read_only":true}]},"RotateApiKeyInput":{"type":"object","properties":{"overlap_hours":{"default":24,"description":"How many hours the old key keeps working, so whatever holds it can switch to the new one: 0 to 168, 24 by default. Never longer than it had left.","type":"integer","minimum":0,"maximum":168},"password":{"type":"string","minLength":1,"maxLength":256,"description":"For a key that acts for a person: their password, entered again. Not needed for a key that acts for nobody."}},"additionalProperties":false,"examples":[{"overlap_hours":24},{"overlap_hours":0,"password":"correct horse battery"}]},"ReportCodeErrorInput":{"type":"object","properties":{"source":{"type":"string","enum":["browser","web_server","agent"],"description":"What met the fault: the web app in a browser, the web app's server, or an agent calling this API."},"operation":{"type":"string","minLength":1,"maxLength":200,"description":"Where it happened: the operationId called, or the screen's path."},"error_name":{"type":"string","minLength":1,"maxLength":200,"description":"The kind of error, as the code that met it names it."},"message":{"type":"string","maxLength":2000,"description":"What the error said."},"stack":{"description":"Where in the code it was thrown, when the caller has a stack.","type":"string","maxLength":8000},"request_id":{"description":"The request_id of the API response that went with it, when there was one.","type":"string","maxLength":100}},"required":["source","operation","error_name","message"],"additionalProperties":false,"examples":[{"source":"agent","operation":"createSalesOrder","error_name":"UnexpectedResponse","message":"The order was created but its lines came back empty, and getSalesOrder shows two lines.","request_id":"0199c4a2-3b1e-7c4d-9a8f-5e6d7c8b9a0f"}]},"AddTradeInInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"allowance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer is allowed for the unit, paid toward the order when it is received, and what the unit comes into stock at. Set and changed only by someone who holds trade_in.appraise at the order's location, and only while the trade is agreed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"equipment_id":{"description":"The customer's equipment record of the unit, when there is one. Left out, it is filled in when the unit is received with this customer's record of the unit it comes in as, if they have one. That record is retired when the unit is received and restored if it is given back. Fixed once the trade is received.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"description":{"type":"string","minLength":1,"maxLength":200,"description":"What the unit is, as the customer and the receipt see it, for example \"2009 Grandee\". Defaults to the year, brand and model, else the item's name."},"brand":{"type":"string","minLength":1,"maxLength":100,"description":"The unit's brand, as the customer describes it."},"model":{"type":"string","minLength":1,"maxLength":100,"description":"The unit's model, as the customer describes it."},"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The serial number on the unit's data plate, uppercase. Confirmed when the unit is received, when it becomes the serialized unit's; with none known then, TRADE- and the trade's number."},"year_made":{"type":"integer","minimum":1950,"maximum":2100,"description":"The year the unit was made, as the customer or the plate says."},"item_condition":{"type":"string","enum":["used","damaged"],"description":"used: worn but working. damaged: needs repair before it can be sold, or is only scrap. schema.org itemCondition."},"condition_notes":{"type":"string","minLength":1,"maxLength":2000,"description":"What the appraisal found: cracks, a failed pump, missing cover."},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"Free-form notes for staff."},"staff_member_id":{"description":"Who appraised the unit. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["item_id","allowance","idempotency_key"],"additionalProperties":false,"examples":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","allowance":"1500.00","brand":"Hot Spring","model":"Grandee","year_made":2009,"serial_number":"HS0912345","item_condition":"used","condition_notes":"Cover torn, shell sound, heater replaced 2019","idempotency_key":"0199a3c4-trade-1042"}]},"UpdateTradeInInput":{"type":"object","properties":{"allowance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer is allowed for the unit, paid toward the order when it is received, and what the unit comes into stock at. Set and changed only by someone who holds trade_in.appraise at the order's location, and only while the trade is agreed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's equipment record of the unit, when there is one. Left out, it is filled in when the unit is received with this customer's record of the unit it comes in as, if they have one. That record is retired when the unit is received and restored if it is given back. Fixed once the trade is received."},"description":{"type":"string","minLength":1,"maxLength":200,"description":"What the unit is, as the customer and the receipt see it, for example \"2009 Grandee\". Defaults to the year, brand and model, else the item's name."},"brand":{"type":"string","minLength":1,"maxLength":100,"description":"The unit's brand, as the customer describes it."},"model":{"type":"string","minLength":1,"maxLength":100,"description":"The unit's model, as the customer describes it."},"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The serial number on the unit's data plate, uppercase. Confirmed when the unit is received, when it becomes the serialized unit's; with none known then, TRADE- and the trade's number."},"year_made":{"type":"integer","minimum":1950,"maximum":2100,"description":"The year the unit was made, as the customer or the plate says."},"item_condition":{"type":"string","enum":["used","damaged"],"description":"used: worn but working. damaged: needs repair before it can be sold, or is only scrap. schema.org itemCondition."},"condition_notes":{"type":"string","minLength":1,"maxLength":2000,"description":"What the appraisal found: cracks, a failed pump, missing cover."},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"Free-form notes for staff."}},"additionalProperties":false,"examples":[{"allowance":"1250.00","condition_notes":"Pump leaks at the union"}]},"ReceiveTradeInInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"serial_number":{"description":"The serial number read off the unit's plate when it is taken, if it differs from what was recorded or none was. Left out: the one recorded, else TRADE- and the trade's number.","type":"string","minLength":1,"maxLength":100}},"additionalProperties":false,"examples":[{},{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","serial_number":"HS0912345"}]},"DisposeTradeInInput":{"type":"object","properties":{"disposal_reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why the unit left stock without being sold: hauled to the dump, scrapped for parts. Setting it takes the unit out of stock as an adjustment. Set once."}},"required":["disposal_reason"],"additionalProperties":false,"examples":[{"disposal_reason":"Shell cracked through; hauled to the county transfer station"}]},"CreateFinancingLenderInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"vendor_id":{"description":"The lender as a vendor, when the business keeps it as one (it sends bills, or is paid). NULL when not.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"deposit_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"funding_days":{"description":"How many days the lender usually takes to pay out, counted from the financed payment, or from the hand-over when funds_after_hand_over. A payout later than that shows as overdue. Defaults to 3.","type":"integer","minimum":0,"maximum":365},"funds_after_hand_over":{"description":"True when the lender pays out only once the goods are handed over, so funding_days counts from the last hand-over on the order. False counts from the financed payment. Defaults to false.","type":"boolean"},"fee_settlement":{"description":"How the lender takes its dealer fee. withheld (the default): it pays out the financed amount less the fee. billed: it pays out the whole amount and bills the fee separately; the fee is then owed to it (Lender Fees Payable) until the bill is paid in the accounting system.","type":"string","enum":["withheld","billed"]},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"Anything staff should know: the dealer number, who to call, how to submit a sale."},"promotions":{"description":"The plans it offers, if known now.","maxItems":50,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The plan's name as staff offer it, unique within the lender."},"description":{"type":"string","minLength":1,"maxLength":1000,"description":"What the customer should be told, for example that interest is charged from the purchase date if the balance is not paid within the promotion."},"term_months":{"type":"integer","minimum":1,"maximum":480,"description":"How many months the customer has to pay."},"apr_percent":{"description":"The yearly interest rate the customer pays over the term, in percent: 0 for no interest, 9.99 for 9.99%. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"dealer_fee_percent":{"description":"The lender's fee to the business, as a percent of what is financed, for example 6.5. Longer promotions usually cost more. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"dealer_fee_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A fixed fee to the business per financed sale, on top of dealer_fee_percent. 0 for none. A decimal string with up to 2 decimal places, for example \"12.50\"."},"minimum_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least the lender finances on this plan. 0 for no minimum. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["name","term_months"],"additionalProperties":false}}},"required":["name","deposit_account_id"],"additionalProperties":false,"examples":[{"name":"Synchrony","deposit_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","funding_days":2,"promotions":[{"name":"12 months, no interest","term_months":12,"dealer_fee_percent":"4.50","minimum_amount":"1000.00"},{"name":"60 months at 7.99%","term_months":60,"apr_percent":"7.990","dealer_fee_percent":"2.00"}]}]},"UpdateFinancingLenderInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"vendor_id":{"description":"The lender as a vendor, when the business keeps it as one (it sends bills, or is paid). NULL when not.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]},"deposit_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"funding_days":{"description":"How many days the lender usually takes to pay out, counted from the financed payment, or from the hand-over when funds_after_hand_over. A payout later than that shows as overdue.","type":"integer","minimum":0,"maximum":365},"funds_after_hand_over":{"description":"True when the lender pays out only once the goods are handed over, so funding_days counts from the last hand-over on the order. False counts from the financed payment.","type":"boolean"},"fee_settlement":{"description":"How the lender takes its dealer fee. withheld (the default): it pays out the financed amount less the fee. billed: it pays out the whole amount and bills the fee separately; the fee is then owed to it (Lender Fees Payable) until the bill is paid in the accounting system.","type":"string","enum":["withheld","billed"]},"notes":{"description":"Anything staff should know: the dealer number, who to call, how to submit a sale.","anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"Anything staff should know: the dealer number, who to call, how to submit a sale."},{"type":"null"}]},"is_active":{"description":"False once the business no longer sells on this lender: no new applications or financed payments. What is already financed is still refunded, reversed and paid out.","type":"boolean"}},"additionalProperties":false,"examples":[{"funding_days":5,"fee_settlement":"billed"},{"is_active":false}]},"AddFinancingPromotionInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The plan's name as staff offer it, unique within the lender."},"description":{"type":"string","minLength":1,"maxLength":1000,"description":"What the customer should be told, for example that interest is charged from the purchase date if the balance is not paid within the promotion."},"term_months":{"type":"integer","minimum":1,"maximum":480,"description":"How many months the customer has to pay."},"apr_percent":{"description":"The yearly interest rate the customer pays over the term, in percent: 0 for no interest, 9.99 for 9.99%. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"dealer_fee_percent":{"description":"The lender's fee to the business, as a percent of what is financed, for example 6.5. Longer promotions usually cost more. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"dealer_fee_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A fixed fee to the business per financed sale, on top of dealer_fee_percent. 0 for none. A decimal string with up to 2 decimal places, for example \"12.50\"."},"minimum_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least the lender finances on this plan. 0 for no minimum. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["name","term_months"],"additionalProperties":false,"examples":[{"name":"24 months, no interest","term_months":24,"dealer_fee_percent":"8.00","minimum_amount":"2500.00"}]},"UpdateFinancingPromotionInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The plan's name as staff offer it, unique within the lender."},"description":{"description":"What the customer should be told, for example that interest is charged from the purchase date if the balance is not paid within the promotion.","anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What the customer should be told, for example that interest is charged from the purchase date if the balance is not paid within the promotion."},{"type":"null"}]},"term_months":{"type":"integer","minimum":1,"maximum":480,"description":"How many months the customer has to pay."},"apr_percent":{"description":"The yearly interest rate the customer pays over the term, in percent: 0 for no interest, 9.99 for 9.99%. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"dealer_fee_percent":{"description":"The lender's fee to the business, as a percent of what is financed, for example 6.5. Longer promotions usually cost more. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"dealer_fee_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A fixed fee to the business per financed sale, on top of dealer_fee_percent. 0 for none. A decimal string with up to 2 decimal places, for example \"12.50\"."},"minimum_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least the lender finances on this plan. 0 for no minimum. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_active":{"description":"False once the lender no longer offers it: no new applications on it.","type":"boolean"}},"additionalProperties":false,"examples":[{"dealer_fee_percent":"5.25"},{"is_active":false}]},"ApplyForFinancingInput":{"type":"object","properties":{"financing_promotion_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The promotion applied for. Can be changed until money has been taken on the application, to another of the same lender's."},"amount_requested":{"description":"How much the customer asked to finance. Defaults to what is left to pay on the order.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's number for the application or the customer's account, as the lender quotes it."},"staff_member_id":{"description":"Who took the application. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example what the lender asked for."},"decision":{"description":"The lender's answer, when it came at once (an instant decision at the counter).","type":"object","properties":{"status":{"type":"string","enum":["approved","declined"],"description":"The lender's answer."},"amount_approved":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most the lender agreed to finance. Set when approved; can be changed while approved, never below what has been financed on it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"approval_expires_on":{"description":"The last day a financed payment can be taken against the approval. NULL when the lender set no end. Money already taken stands after it.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["status"],"additionalProperties":false}},"required":["financing_promotion_id"],"additionalProperties":false,"examples":[{"financing_promotion_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount_requested":"9500.00","reference":"APP-55120","decision":{"status":"approved","amount_approved":"10000.00","approval_expires_on":"2026-11-30"}},{"financing_promotion_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"UpdateFinancingApplicationInput":{"type":"object","properties":{"financing_promotion_id":{"description":"The promotion applied for. Can be changed until money has been taken on the application, to another of the same lender's. Another of the same lender's.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"amount_requested":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much the customer asked to finance. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"The lender's number for the application or the customer's account, as the lender quotes it.","anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The lender's number for the application or the customer's account, as the lender quotes it."},{"type":"null"}]},"note":{"description":"Anything worth recording, for example what the lender asked for.","anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example what the lender asked for."},{"type":"null"}]}},"additionalProperties":false,"examples":[{"financing_promotion_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"reference":"ACCT-0042"}]},"RecordFinancingDecisionInput":{"type":"object","properties":{"status":{"type":"string","enum":["approved","declined"],"description":"approved: the lender agreed (again, to change the amount or the expiry of an approval). declined: it refused, or withdrew its approval; whatever was taken through it that the lender has not paid out is reversed, and the customer owes it again."},"amount_approved":{"description":"The most the lender agreed to finance. Set when approved; can be changed while approved, never below what has been financed on it. Needed to approve, unless already approved.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"approval_expires_on":{"description":"The last day a financed payment can be taken against the approval. NULL when the lender set no end. Money already taken stands after it. Send null to clear it.","anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}]},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's number for the application or the customer's account, as the lender quotes it."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example what the lender asked for."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["status","idempotency_key"],"additionalProperties":false,"examples":[{"status":"approved","amount_approved":"10000.00","approval_expires_on":"2026-11-30","reference":"APP-55120","idempotency_key":"0199a3c4-approve-1042"},{"status":"declined","note":"Lender could not verify income","idempotency_key":"0199a3c4-decline-1042"}]},"CancelFinancingApplicationInput":{"type":"object","properties":{"note":{"description":"Why, for example 'Paying cash instead'.","type":"string","minLength":1,"maxLength":1000},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"note":"Customer is paying by check instead","idempotency_key":"0199a3c4-cancel-fin-1042"}]},"RecordFinancingFundingInput":{"type":"object","properties":{"funded_on":{"description":"The date the money arrived or left, as on the lender's statement or the bank's. Not after today at the order's location, and not in a closed period. Defaults to today.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's reference for the payout or the deposit, to match it to the bank."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example why the lender took money back."},"fundings":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"financing_application_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The application the payout is for. It must be approved or funded."},"amount":{"description":"How much of what the lender owes this settles, before its fee: positive for a payout, no more than it still owes on the application; negative when it takes money back, no more than it has paid out on it. Defaults to everything the lender still owes on it.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"dealer_fee":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The lender's fee on this payout, the same sign as amount (a negative fee is a fee given back). Left out, it is the promotion's fee on what is now paid out less the fees already recorded on the application. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_received":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What arrived for this sale, as on the lender's statement: the fee is then amount less this, for a lender that withholds its fee. Send this or dealer_fee, not both. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["financing_application_id"],"additionalProperties":false},"description":"One row per sale the payout covers, as the lender's statement lists them."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["fundings","idempotency_key"],"additionalProperties":false,"examples":[{"funded_on":"2026-10-05","reference":"ACH 88231","fundings":[{"financing_application_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"financing_application_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","amount":"6200.00","amount_received":"5921.00"}],"idempotency_key":"0199a3c4-payout-1005"}]},"SellGiftCardInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer puts on the card. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"For a sale, reload, void, reversal or cash-out: how the money came in or went back. Cash, a check, a card or another method of the business's own; never store credit, account credit, a lender or a gift card. A void and a reversal go back by the method of the sale, and a reversal never by cash, which cannot bounce."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the money: a card processor's transaction id, a card's last four digits, a check number. Never a full card number: one is refused."},"code":{"description":"The code printed on a card from the business's stock, sent with its PIN: 8 to 32 letters and digits, spaces and dashes not counted. Leave it out to have a 16-digit random code made, returned once, which needs no PIN.","type":"string","minLength":8,"maxLength":64},"pin":{"description":"The PIN printed with the card's code. Needed with code; leave both out to have a code made.","type":"string","pattern":"^\\d{4,8}$"},"recipient_name":{"type":"string","minLength":1,"maxLength":200,"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label."},"purchaser_customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","payment_method_id","location_id","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"100.00","payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","recipient_name":"Grandma Ruth","idempotency_key":"0199a3c4-giftcard-sale-1"},{"amount":"50.00","payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","code":"6035 7172 0093 1177","idempotency_key":"0199a3c4-giftcard-sale-2"}]},"ReloadGiftCardInput":{"type":"object","properties":{"gift_card":{"type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":64,"description":"The card's code, as printed or as it was given when the card was sold: 8 to 32 letters and digits, spaces and dashes not counted. Never stored or shown again."},"pin":{"type":"string","pattern":"^\\d{4,8}$","description":"The card's PIN, for a card that has one: 4 to 8 digits."}},"required":["code"],"additionalProperties":false,"description":"The gift card, by its code and PIN as on the card. Its id or number never spends it. Every wrong code counts against this API key: ten in fifteen minutes lock it out of gift cards for fifteen minutes."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer adds to the card. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"For a sale, reload, void, reversal or cash-out: how the money came in or went back. Cash, a check, a card or another method of the business's own; never store credit, account credit, a lender or a gift card. A void and a reversal go back by the method of the sale, and a reversal never by cash, which cannot bounce."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the money: a card processor's transaction id, a card's last four digits, a check number. Never a full card number: one is refused."},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["gift_card","amount","payment_method_id","location_id","idempotency_key"],"additionalProperties":false,"examples":[{"gift_card":{"code":"6035717200931177"},"amount":"25.00","payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","idempotency_key":"0199a3c4-giftcard-reload-1"}]},"CheckGiftCardBalanceInput":{"type":"object","properties":{"gift_card":{"type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":64,"description":"The card's code, as printed or as it was given when the card was sold: 8 to 32 letters and digits, spaces and dashes not counted. Never stored or shown again."},"pin":{"type":"string","pattern":"^\\d{4,8}$","description":"The card's PIN, for a card that has one: 4 to 8 digits."}},"required":["code"],"additionalProperties":false,"description":"The gift card, by its code and PIN as on the card. Its id or number never spends it. Every wrong code counts against this API key: ten in fifteen minutes lock it out of gift cards for fifteen minutes."}},"required":["gift_card"],"additionalProperties":false,"examples":[{"gift_card":{"code":"6035717200931177"}},{"gift_card":{"code":"GC7Q2K9M","pin":"4821"}}]},"GivePromotionalGiftCardInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the card holds. A decimal string with up to 2 decimal places, for example \"12.50\"."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"code":{"description":"The code printed on a card from the business's stock, sent with its PIN: 8 to 32 letters and digits, spaces and dashes not counted. Leave it out to have a 16-digit random code made, returned once, which needs no PIN.","type":"string","minLength":8,"maxLength":64},"pin":{"description":"The PIN printed with the card's code. Needed with code; leave both out to have a code made.","type":"string","pattern":"^\\d{4,8}$"},"recipient_name":{"type":"string","minLength":1,"maxLength":200,"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label."},"purchaser_customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why it is given, for example the event or the complaint."},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","ledger_account_id","location_id","note","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"25.00","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","note":"Home and Garden Show raffle","idempotency_key":"0199a3c4-giftcard-promo-1"}]},"VoidGiftCardSaleInput":{"type":"object","properties":{"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why, for example 'Rang up 500 instead of 50'."},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["note","idempotency_key"],"additionalProperties":false,"examples":[{"note":"Customer changed their mind","idempotency_key":"0199a3c4-giftcard-void-1"}]},"ReverseGiftCardSaleInput":{"type":"object","properties":{"sequence":{"description":"The sale or reload whose money never arrived, by its place among this card's entries (sequence, as getGiftCard lists them); for one made on a card since replaced, name that card in the path. Leave it out for the latest sale or reload by check, card or another method not yet voided or reversed, on this card or the cards that replaced it or it replaced.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why, for example 'Check 4417 returned NSF'."},"reference":{"description":"The outside reference for the money: a card processor's transaction id, a card's last four digits, a check number. Never a full card number: one is refused.","type":"string","minLength":1,"maxLength":200},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["note","idempotency_key"],"additionalProperties":false,"examples":[{"note":"Check 4417 returned NSF","idempotency_key":"0199a3c4-giftcard-reverse-1"}]},"ReplaceGiftCardInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"code":{"description":"The code printed on a card from the business's stock, sent with its PIN: 8 to 32 letters and digits, spaces and dashes not counted. Leave it out to have a 16-digit random code made, returned once, which needs no PIN.","type":"string","minLength":8,"maxLength":64},"pin":{"description":"The PIN printed with the card's code. Needed with code; leave both out to have a code made.","type":"string","pattern":"^\\d{4,8}$"},"recipient_name":{"type":"string","minLength":1,"maxLength":200,"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why, for example 'Lost; purchaser showed the receipt'."},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["location_id","note","idempotency_key"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","note":"Card cracked; holder showed it at the counter","idempotency_key":"0199a3c4-giftcard-replace-1"}]},"CashOutGiftCardInput":{"type":"object","properties":{"gift_card":{"type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":64,"description":"The card's code, as printed or as it was given when the card was sold: 8 to 32 letters and digits, spaces and dashes not counted. Never stored or shown again."},"pin":{"type":"string","pattern":"^\\d{4,8}$","description":"The card's PIN, for a card that has one: 4 to 8 digits."}},"required":["code"],"additionalProperties":false,"description":"The gift card, by its code and PIN as on the card. Its id or number never spends it. Every wrong code counts against this API key: ten in fifteen minutes lock it out of gift cards for fifteen minutes."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"For a sale, reload, void, reversal or cash-out: how the money came in or went back. Cash, a check, a card or another method of the business's own; never store credit, account credit, a lender or a gift card. A void and a reversal go back by the method of the sale, and a reversal never by cash, which cannot bounce."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"Paid out by check: the number of the check the business writes. Needed while the business requires check numbers (getPolicy, check_number_required)."},"staff_member_id":{"description":"Who did it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["gift_card","payment_method_id","location_id","idempotency_key"],"additionalProperties":false,"examples":[{"gift_card":{"code":"6035717200931177"},"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","idempotency_key":"0199a3c4-giftcard-cashout-1"},{"gift_card":{"code":"6035717200931177"},"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","reference":"5121","idempotency_key":"0199a3c4-giftcard-cashout-2"}]},"WriteOffGiftCardInput":{"type":"object","properties":{"reason":{"type":"string","enum":["breakage","escheat"],"description":"breakage: taken into income as never to be spent, only once the card has gone unused for the business's dormancy period (getPolicy, gift_card_breakage_after_days). escheat: owed to the state as unclaimed property, moved to the liability account named until it is paid, only once the card has gone unused for the state's dormancy period (getPolicy, gift_card_escheat_after_days)."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why, for example the state report it was on."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["reason","location_id","note","idempotency_key"],"additionalProperties":false,"examples":[{"reason":"escheat","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","note":"Idaho unclaimed property report 2027","idempotency_key":"0199a3c4-giftcard-escheat-1"}]},"UpdateGiftCardInput":{"type":"object","properties":{"status":{"type":"string","enum":["active","blocked"],"description":"blocked stops it being spent, for a card reported lost or stolen; anyone may block one. active unblocks it, which needs giftcard.manage where it was sold. To close a card, replace it or write it off."},"status_note":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"Why the card is blocked, for example \"Reported lost by the purchaser, 2026-11-02\"."},{"type":"null"}],"description":"Why the card is blocked, for example \"Reported lost by the purchaser, 2026-11-02\"."},"recipient_name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label."},{"type":"null"}],"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label."},"purchaser_customer_id":{"anyOf":[{"type":"string","x-natural-key":"customer_number"},{"type":"null"}],"description":"The customer who bought or was given the card, when the business knows. The card is not theirs to spend: whoever holds the code spends it. They owe what was spent of a sale whose money never arrived (a reversal), so it is changed only with giftcard.manage, and never once a sale or reload on the card (or a card it replaced) was paid by a method other than cash. Accepts the record's id or its customer number (as a number or a string of digits).","x-natural-key":"customer_number"},"clear_pin_lock":{"type":"boolean","const":true,"description":"Clear the lock five wrong PINs put on the card, and its count of wrong PINs, once the holder has shown it is theirs. Needs giftcard.manage where it was sold."}},"additionalProperties":false,"examples":[{"status":"blocked","status_note":"Reported lost by the purchaser"},{"clear_pin_lock":true}]},"CreatePackageInput":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":40,"description":"The business's own code for the package, uppercase, unique within the tenant, as staff quote it."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the package is called on the order and in advertising."},"description":{"type":"string","minLength":1,"maxLength":5000,"description":"Longer description for quotes and the web store."},"price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The one price the package is advertised at, before tax, with every slot's own item. A choice in a slot can add to it. Taken onto an order when the package is added, so a later change does not reprice orders already written. A decimal string with up to 2 decimal places, for example \"12.50\"."},"components":{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What the slot is called, unique in the package whatever its case, for example Cover. Orders name the slot by it."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many of the item, in its unit: 1 for a serial-tracked item. Defaults to 1.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"position":{"type":"integer","minimum":0,"maximum":1000,"description":"Where the slot comes in the package, smallest first: the order of its lines on an order."},"can_drop":{"type":"boolean","description":"True when the customer may leave the slot out, for example steps they already have. What that does to the package price is the business's policy (package_drop_credit). False for a slot the package is not sold without."},"is_main":{"type":"boolean","description":"True for the slot the package is built around, the spa: the one a package discount comes off first when the business shares discounts that way (package_discount_allocation main_component). At most one active slot per package. Making one slot main makes the package's other slots not main."},"choices":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"price_difference":{"description":"What choosing it adds to the package price, before tax: 0 for a like-for-like choice, negative for one that costs less. Taken onto the order when it is chosen. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"is_active":{"description":"False once it is no longer offered in the slot. Orders that have it keep it.","type":"boolean"}},"required":["item_id"],"additionalProperties":false},"description":"Other items the customer may have in the slot, each added or changed by item. Others are left as they are."}},"required":["name","item_id"],"additionalProperties":false},"description":"The slots, in the order they appear unless position says otherwise."}},"required":["code","name","price","components"],"additionalProperties":false,"examples":[{"code":"GRANDEE-PKG","name":"Grandee Complete","price":"18999.00","components":[{"name":"Spa","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","is_main":true},{"name":"Cover","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","choices":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"}]},{"name":"Steps","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","can_drop":true},{"name":"Starter chemicals","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"name":"Delivery","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]}]},"UpdatePackageInput":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":40,"description":"The business's own code for the package, uppercase, unique within the tenant, as staff quote it."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the package is called on the order and in advertising."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Longer description for quotes and the web store."},{"type":"null"}],"description":"Longer description for quotes and the web store."},"price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The one price the package is advertised at, before tax, with every slot's own item. A choice in a slot can add to it. Taken onto an order when the package is added, so a later change does not reprice orders already written. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_active":{"type":"boolean","description":"False once the package is no longer offered. Orders that have it keep it."}},"additionalProperties":false,"examples":[{"price":"18499.00"},{"is_active":false}]},"SetPackageComponentInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What the slot is called, unique in the package whatever its case, for example Cover. Orders name the slot by it."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item, in its unit: 1 for a serial-tracked item. A decimal string with up to 3 decimal places, for example \"12.500\"."},"position":{"type":"integer","minimum":0,"maximum":1000,"description":"Where the slot comes in the package, smallest first: the order of its lines on an order."},"can_drop":{"type":"boolean","description":"True when the customer may leave the slot out, for example steps they already have. What that does to the package price is the business's policy (package_drop_credit). False for a slot the package is not sold without."},"is_main":{"type":"boolean","description":"True for the slot the package is built around, the spa: the one a package discount comes off first when the business shares discounts that way (package_discount_allocation main_component). At most one active slot per package. Making one slot main makes the package's other slots not main."},"is_active":{"type":"boolean","description":"False once the slot is no longer part of the package. Orders that already have it keep it."},"choices":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"price_difference":{"description":"What choosing it adds to the package price, before tax: 0 for a like-for-like choice, negative for one that costs less. Taken onto the order when it is chosen. Defaults to 0.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"is_active":{"description":"False once it is no longer offered in the slot. Orders that have it keep it.","type":"boolean"}},"required":["item_id"],"additionalProperties":false},"description":"Other items the customer may have in the slot, each added or changed by item. Others are left as they are."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Cover lifter","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","can_drop":true},{"name":"Cover","choices":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","price_difference":"150.00"}]},{"name":"Steps","is_active":false}]},"AddSalesOrderPackageInput":{"type":"object","properties":{"package_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its package code."},"discount_amount":{"description":"Money taken off this package on this order, beyond the package's own pricing. At most list_price. Beyond what this key's roles allow, needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"components":{"maxItems":30,"type":"array","items":{"type":"object","properties":{"component":{"type":"string","minLength":1,"maxLength":100,"description":"The slot, by name (case does not matter), as the package names it: Cover, Steps."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"dropped":{"description":"True when the customer left the slot out, which the slot must allow (can_drop): its line is dropped. Set back to false to have it after all, while the package can still change. Only a slot with can_drop. What it does to the price is the business's policy (getPolicy, package_drop_credit).","type":"boolean"},"options":{"description":"For a model built with options: how the customer's unit is to be built, as on an order line. The whole set, every group chosen, not just the groups that change: it replaces the options the line had. null clears them, so a unit named takes its own.","anyOf":[{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false},"description":"For a model built with options (getItem, option_groups): one option from each group chosen, every required group answered. Each option's price difference is added to the price."},{"type":"null"}]},"serialized_unit_id":{"description":"The unit the customer gets in the slot, as serialized_unit_id on an order line.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]}},"required":["component"],"additionalProperties":false},"description":"What the customer has in each slot named, if not the slot's own item as it comes."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["package_id"],"additionalProperties":false,"examples":[{"package_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"package_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","components":[{"component":"Spa","options":[{"group":"Shell color","option":"Tuscan Sun"}]},{"component":"Cover","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"},{"component":"Steps","dropped":true}]}]},"UpdateSalesOrderPackageInput":{"type":"object","properties":{"discount_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money taken off this package on this order, beyond the package's own pricing. At most list_price. Beyond what this key's roles allow, needs order.discount."},"remove":{"type":"boolean","const":true,"description":"true takes the whole package off the order, while none of it has been handed over. Final."},"components":{"maxItems":30,"type":"array","items":{"type":"object","properties":{"component":{"type":"string","minLength":1,"maxLength":100,"description":"The slot, by name (case does not matter), as the package names it: Cover, Steps."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"dropped":{"description":"True when the customer left the slot out, which the slot must allow (can_drop): its line is dropped. Set back to false to have it after all, while the package can still change. Only a slot with can_drop. What it does to the price is the business's policy (getPolicy, package_drop_credit).","type":"boolean"},"options":{"description":"For a model built with options: how the customer's unit is to be built, as on an order line. The whole set, every group chosen, not just the groups that change: it replaces the options the line had. null clears them, so a unit named takes its own.","anyOf":[{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false},"description":"For a model built with options (getItem, option_groups): one option from each group chosen, every required group answered. Each option's price difference is added to the price."},{"type":"null"}]},"serialized_unit_id":{"description":"The unit the customer gets in the slot, as serialized_unit_id on an order line.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]}},"required":["component"],"additionalProperties":false},"description":"What the customer has in each slot named, if not the slot's own item as it comes."}},"additionalProperties":false,"examples":[{"components":[{"component":"Cover","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"}]},{"components":[{"component":"Spa","serialized_unit_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},{"components":[{"component":"Steps","dropped":true}]},{"discount_amount":"500.00"},{"remove":true}]},"CreateSalesOrderInput":{"type":"object","properties":{"status":{"default":"quote","description":"quote to offer prices the customer has not yet agreed to, open when they have agreed.","type":"string","enum":["quote","open"]},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"site_id":{"description":"Where the goods are to be delivered: one of the customer's own sites. NULL when the customer takes them at the location.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"order_date":{"description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"expires_on":{"description":"For a quote: the last date the offered prices hold, and the last date it holds any serialized unit its lines name (when the business's quotes hold units, tenant_policy.quote_holds_units). After it, the units are free to sell to anyone; a new expires_on holds them again if no one has taken them. A new quote written up without one gets the order date plus the business's quote_valid_days, when it has set them.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"tax_area_id":{"description":"The tax area the sale is taxed in. Defaults to the delivery site's area, or the location's when there is no site or the site has none. NULL means no tax is charged.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"is_tax_exempt":{"description":"True when no tax is charged on this order. Starts true for a tax-exempt customer when one of their certificates covers the order's date and state, or when none does and the business sells to them untaxed anyway (tenant_policy.exemption_without_certificate). Setting it true without a certificate that covers it needs tax.override.","type":"boolean"},"sales_channel_id":{"description":"The channel the order came through. NULL when not recorded.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"lead_id":{"type":"string","x-natural-key":"lead_number","description":"Accepts the record's id or its lead number (as a number or a string of digits)."},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff."},"lines":{"default":[],"description":"What the customer is buying.","maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many the customer is buying, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. May be left out when the item is sold in only one pack.","type":"string","minLength":1,"maxLength":40},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"discount_amount":{"description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. Left out with a unit named, the line takes the unit's options. Each option's price difference is added to the item's price unless unit_price is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false}},"credits":{"description":"Who gets credit for the sale. Defaults to the staff member this API key acts for, with share 1. Send an empty list for a sale nobody is credited with.","maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who gets the credit."},"share":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"Their part of the sale, as a fraction of 1. Set to 0 to take someone off the sale. A decimal string with up to 4 decimal places, for example \"12.5000\"."}},"required":["staff_member_id","share"],"additionalProperties":false}}},"required":["location_id"],"additionalProperties":false,"examples":[{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","expires_on":"2026-10-31","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","quantity":"1"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7f","quantity":"1","unit_price":"450.00"}]},{"status":"open","customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","quantity":"2"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7f","packs":2,"pack_text":"case"}],"credits":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","share":"0.6"},{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","share":"0.4"}]}]},"UpdateSalesOrderInput":{"type":"object","properties":{"customer_id":{"anyOf":[{"type":"string","x-natural-key":"customer_number"},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over. Accepts the record's id or its customer number (as a number or a string of digits).","x-natural-key":"customer_number"},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the goods are to be delivered: one of the customer's own sites. NULL when the customer takes them at the location."},"order_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over."},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a quote: the last date the offered prices hold, and the last date it holds any serialized unit its lines name (when the business's quotes hold units, tenant_policy.quote_holds_units). After it, the units are free to sell to anyone; a new expires_on holds them again if no one has taken them. A new quote written up without one gets the order date plus the business's quote_valid_days, when it has set them."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the sale is taxed in. Defaults to the delivery site's area, or the location's when there is no site or the site has none. NULL means no tax is charged."},"is_tax_exempt":{"type":"boolean","description":"True when no tax is charged on this order. Starts true for a tax-exempt customer when one of their certificates covers the order's date and state, or when none does and the business sells to them untaxed anyway (tenant_policy.exemption_without_certificate). Setting it true without a certificate that covers it needs tax.override."},"sales_channel_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The channel the order came through. NULL when not recorded."},"lead_id":{"anyOf":[{"type":"string","x-natural-key":"lead_number"},{"type":"null"}],"description":"The customer's lead this quote or order came from, named by whoever writes it up: never assumed, so a customer with a spa lead open who buys chemicals has not bought the spa. Agreeing to an order linked to a lead (opening it, or linking it once open) wins the lead. Fixed once the order is agreed on it. Takes the lead number. Linking an agreed order wins the lead; a quote can be unlinked (null). Accepts the record's id or its lead number (as a number or a string of digits).","x-natural-key":"lead_number"},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff."},{"type":"null"}],"description":"Free-form notes for staff."}},"additionalProperties":false,"examples":[{"site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"expires_on":"2026-11-15","notes":"Holding price through the home show."}]},"AddSalesOrderLinesInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many the customer is buying, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. May be left out when the item is sold in only one pack.","type":"string","minLength":1,"maxLength":40},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"discount_amount":{"description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. Left out with a unit named, the line takes the unit's options. Each option's price difference is added to the item's price unless unit_price is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false},"description":"The lines to add."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"1"}]}]},"UpdateSalesOrderLineInput":{"type":"object","properties":{"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many the customer is buying, in the item's unit. 0 drops the line. Never negative: for goods the customer brings back, use returnSalesOrderGoods on the order they were sold on. A decimal string with up to 3 decimal places, for example \"12.500\"."},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"type":"string","minLength":1,"maxLength":40,"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. Sent with packs to sell a line in units by a pack instead, when the item has several."},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount."},"discount_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives."},"options":{"anyOf":[{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false},"description":"For a model built with options (getItem, option_groups): one option from each group chosen, every required group answered. Each option's price difference is added to the price."},{"type":"null"}],"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. The difference in the options' prices is added to unit_price unless unit_price is sent. null clears them: sent with a unit, the line takes the unit's options and their prices."}},"additionalProperties":false,"examples":[{"serialized_unit_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"quantity":"0"},{"packs":3},{"discount_amount":"500.00"},{"options":[{"group":"Shell color","option":"Tuscan Sun"}]},{"serialized_unit_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","options":null}]},"SetSalesCreditsInput":{"type":"object","properties":{"credits":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who gets the credit."},"share":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"Their part of the sale, as a fraction of 1. Set to 0 to take someone off the sale. A decimal string with up to 4 decimal places, for example \"12.5000\"."}},"required":["staff_member_id","share"],"additionalProperties":false},"description":"Everyone credited with the sale and their shares, which must add up to 1 (or send none). Replaces the credit on the order."}},"required":["credits"],"additionalProperties":false,"examples":[{"credits":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","share":"1"}]}]},"FulfilSalesOrderInput":{"type":"object","properties":{"lines":{"description":"What is handed over now. Omit to hand over everything still to be handed over.","maxItems":200,"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line, from the order's lines."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many. More than 0. A decimal string with up to 3 decimal places, for example \"12.500\"."},"lot_code":{"description":"For an item tracked by lot (lot_tracked): handed over, the lot taken (left out, the first lots to expire go, then the oldest); returned, the lot the goods come back into, one they left from (left out, the one they left from, when that was one lot). Goods from two lots are two lines. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["sales_order_line_id","quantity"],"additionalProperties":false}},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who handed the goods over or took them back. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example why goods were returned."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"idempotency_key":"0199a3c4-handover-1042"},{"lines":[{"sales_order_line_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"2"}],"idempotency_key":"0199a3c4-handover-1042-part"}]},"ReturnSalesOrderGoodsInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line, from the order's lines."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many. More than 0. A decimal string with up to 3 decimal places, for example \"12.500\"."},"lot_code":{"description":"For an item tracked by lot (lot_tracked): handed over, the lot taken (left out, the first lots to expire go, then the oldest); returned, the lot the goods come back into, one they left from (left out, the one they left from, when that was one lot). Goods from two lots are two lines. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["sales_order_line_id","quantity"],"additionalProperties":false},"description":"What the customer brought back, and how many of each."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who took the return. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"description":"Why they came back.","type":"string","minLength":1,"maxLength":1000},"restock":{"description":"On a return: true (the default) when the goods go back into stock to sell again, at what the hand-overs they come off cost. False when they cannot be sold again (opened, used, damaged): nothing comes into stock and the cost of the sale stands, as for a return without a receipt. Never false for a serial-tracked unit, which is written down on its own. Always true on a hand-over. Defaults to true. A unit tracked by serial number comes back as itself: return it, then write it down with writeDownSerializedUnit.","type":"boolean"},"waive_restocking_fee":{"description":"True to take the goods back without the business's restocking fee, for example when they are faulty. Needs order.discount where the goods come back.","type":"boolean"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["lines","idempotency_key"],"additionalProperties":false,"examples":[{"lines":[{"sales_order_line_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"1"}],"note":"Wrong size","idempotency_key":"0199a3c4-return-1042"}]},"TakePaymentInput":{"type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid. This decides which account the money is recorded in."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"financing_application_id":{"description":"For a payment by a lender's method: the approved application it is taken against. Left out, the order's one approved application with that lender is used (for a refund, the one holding financed money). A reversal takes the one of the payment it reverses. NULL for every other method.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who took the payment or gave the refund. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example why a refund was given."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much was received. Omit to pay what is left on the order (balance_due). A decimal string with up to 2 decimal places, for example \"12.50\"."},"gift_card":{"description":"For the Gift card method: the card's code and PIN, as on the card. A card pays only when its code is sent; its id or number never spends it. Every wrong code counts against this API key: ten in fifteen minutes lock it out of gift cards for fifteen minutes.","type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":64,"description":"The card's code, as printed or as it was given when the card was sold: 8 to 32 letters and digits, spaces and dashes not counted. Never stored or shown again."},"pin":{"type":"string","pattern":"^\\d{4,8}$","description":"The card's PIN, for a card that has one: 4 to 8 digits."}},"required":["code"],"additionalProperties":false}},"required":["payment_method_id","idempotency_key"],"additionalProperties":false,"examples":[{"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount":"2000.00","reference":"ch_3Q8x","idempotency_key":"0199a3c4-deposit-1042"},{"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-balance-1042"}]},"RefundPaymentInput":{"type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid. This decides which account the money is recorded in."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"financing_application_id":{"description":"For a payment by a lender's method: the approved application it is taken against. Left out, the order's one approved application with that lender is used (for a refund, the one holding financed money). A reversal takes the one of the payment it reverses. NULL for every other method.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who took the payment or gave the refund. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording, for example why a refund was given."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much to give back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"gift_card_id":{"type":"string","x-natural-key":"card_number","description":"Accepts the record's id or its card number (as a number or a string of digits)."},"new_gift_card":{"description":"For the Gift card method: put the money onto a new card from the stock instead, handed to the customer. It takes first the gift card value the order holds, whatever card paid it, as the holder's own, which needs giftcard.manage where it is handed over; what a card now blocked paid goes onto no new card until that card is replaced (replaceGiftCard). Beyond that it is new value: the business must sell gift cards, and it takes only money the order took by cash, check, card or another method of the business's own, up to the daily limit per card (getPolicy, gift_card_max_load).","type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":64,"description":"The code printed on a card from the business's stock: 8 to 32 letters and digits, spaces and dashes not counted. A card from the stock always needs its PIN (pin) as well: printed codes often run in sequence, so one card's code tells its neighbours'."},"pin":{"type":"string","pattern":"^\\d{4,8}$","description":"The PIN printed with the card's code."},"recipient_name":{"type":"string","minLength":1,"maxLength":200,"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label."}},"required":["code","pin"],"additionalProperties":false}},"required":["payment_method_id","idempotency_key","amount"],"additionalProperties":false,"examples":[{"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount":"54.99","note":"Returned unopened","idempotency_key":"0199a3c4-refund-1042"}]},"OpenSalesOrderInput":{"type":"object","properties":{"deposit":{"description":"Money the customer pays down as the quote becomes an order, taken in the same step. When the business asks for a deposit before an order opens (getPolicy, min_deposit_percent), it must bring what is paid up to that share of the total.","type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid. This decides which account the money is recorded in."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much the customer pays down. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."}},"required":["payment_method_id","amount"],"additionalProperties":false},"idempotency_key":{"description":"Required with deposit: a key you make up and send again on any retry, so it is taken once.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"additionalProperties":false,"examples":[{},{"deposit":{"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount":"2500.00","reference":"ch_3Q8x"},"idempotency_key":"0199a3c4-open-1042"}]},"ReversePaymentInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much of the payment did not arrive. Omit for all that is left of it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"description":"What happened, for example 'Check returned NSF'.","type":"string","minLength":1,"maxLength":1000},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"note":"Check returned NSF","idempotency_key":"0199a3c4-nsf-2208"}]},"RecordCounterSaleInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"is_tax_exempt":{"description":"Send true to sell it untaxed although no certificate of the customer's covers it (tax_exemption_status certificate_lapsed from priceCounterSale). Needs tax.override at the location.","type":"boolean","const":true},"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many the customer is buying, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. May be left out when the item is sold in only one pack.","type":"string","minLength":1,"maxLength":40},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"discount_amount":{"description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. Left out with a unit named, the line takes the unit's options. Each option's price difference is added to the item's price unless unit_price is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false},"description":"What the customer is buying."},"payments":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid. This decides which account the money is recorded in."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much by this method. Omit on one payment to have it pay whatever is left. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"gift_card":{"description":"For the Gift card method: the card's code and PIN, as on the card. A card pays only when its code is sent; its id or number never spends it. Every wrong code counts against this API key: ten in fifteen minutes lock it out of gift cards for fifteen minutes.","type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":64,"description":"The card's code, as printed or as it was given when the card was sold: 8 to 32 letters and digits, spaces and dashes not counted. Never stored or shown again."},"pin":{"type":"string","pattern":"^\\d{4,8}$","description":"The card's PIN, for a card that has one: 4 to 8 digits."}},"required":["code"],"additionalProperties":false}},"required":["payment_method_id"],"additionalProperties":false},"description":"How the customer paid. Together they must pay the whole total."},"credits":{"description":"Who gets credit. Defaults to the staff member this API key acts for, with share 1.","maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who gets the credit."},"share":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"Their part of the sale, as a fraction of 1. Set to 0 to take someone off the sale. A decimal string with up to 4 decimal places, for example \"12.5000\"."}},"required":["staff_member_id","share"],"additionalProperties":false}},"notes":{"type":"string","minLength":1,"maxLength":1000,"description":"Free-form notes for staff."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["location_id","lines","payments","idempotency_key"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","quantity":"2"}],"payments":[{"payment_method_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d"}],"idempotency_key":"0199a3c4-counter-boi-0931"}]},"RecordCounterSaleOnReaderInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"is_tax_exempt":{"description":"Send true to sell it untaxed although no certificate of the customer's covers it (tax_exemption_status certificate_lapsed from priceCounterSale). Needs tax.override at the location.","type":"boolean","const":true},"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many the customer is buying, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. May be left out when the item is sold in only one pack.","type":"string","minLength":1,"maxLength":40},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"discount_amount":{"description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. Left out with a unit named, the line takes the unit's options. Each option's price difference is added to the item's price unless unit_price is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false},"description":"What the customer is buying."},"card_reader_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The reader at this counter to send the card to (listCardReaders). It takes what the other payments leave."},"payments":{"default":[],"description":"Any part paid some other way first (cash, a check), each with its amount: the card takes the rest. Recorded only if the card is approved. A gift card cannot share a ticket with the reader: its code would have to be kept until the card is approved.","maxItems":4,"type":"array","items":{"type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid. This decides which account the money is recorded in."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much by this method. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."}},"required":["payment_method_id","amount"],"additionalProperties":false}},"credits":{"description":"Who gets credit. Defaults to the staff member this API key acts for, with share 1.","maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who gets the credit."},"share":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"Their part of the sale, as a fraction of 1. Set to 0 to take someone off the sale. A decimal string with up to 4 decimal places, for example \"12.5000\"."}},"required":["staff_member_id","share"],"additionalProperties":false}},"notes":{"type":"string","minLength":1,"maxLength":1000,"description":"Free-form notes for staff."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["location_id","lines","card_reader_id","idempotency_key"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","quantity":"2"}],"card_reader_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","idempotency_key":"0199a3c4-counter-boi-0932"}]},"PriceCounterSaleInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"is_tax_exempt":{"description":"Send true to sell it untaxed although no certificate of the customer's covers it (tax_exemption_status certificate_lapsed from priceCounterSale). Needs tax.override at the location.","type":"boolean","const":true},"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many the customer is buying, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. May be left out when the item is sold in only one pack.","type":"string","minLength":1,"maxLength":40},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"discount_amount":{"description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. Left out with a unit named, the line takes the unit's options. Each option's price difference is added to the item's price unless unit_price is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false},"description":"What the customer is buying, as recordCounterSale takes it."}},"required":["location_id","lines"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","quantity":"2"}]}]},"EmailQuoteInput":{"type":"object","properties":{"contact_point_id":{"description":"The customer's email address to send it to (getCustomer, contact_points). Defaults to their primary email.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"description":"A key you make up and send again on a retry, so the email is written once.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"additionalProperties":false,"examples":[{},{"contact_point_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-quote-email-1042"}]},"RegisterCardReaderInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"What staff call it, for example \"Front counter\" or \"Delivery truck 2\"."},"registration_code":{"type":"string","minLength":3,"maxLength":100,"description":"The code the reader shows on its screen when it is ready to be registered (on a Stripe reader: settings, then Generate pairing code)."},"carried_by_staff_member_id":{"description":"The technician or delivery driver who carries this reader on the road (a cellular or hotspot reader), so their screens at a customer's door offer it. NULL for a reader that stays at the store's counter.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["location_id","name","registration_code"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","name":"Front counter","registration_code":"sepia-cerulean-aqua"}]},"UpdateCardReaderInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What staff call it, for example \"Front counter\" or \"Delivery truck 2\"."},"is_active":{"description":"False once the reader is no longer used: no new charges are sent to it.","type":"boolean"},"carried_by_staff_member_id":{"description":"The technician or delivery driver who carries this reader on the road (a cellular or hotspot reader), so their screens at a customer's door offer it. NULL for a reader that stays at the store's counter.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]}},"additionalProperties":false,"examples":[{"name":"Delivery truck 2"},{"is_active":false},{"carried_by_staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"TakeCardOnReaderInput":{"type":"object","properties":{"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"card_reader_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The reader to send the charge to (listCardReaders)."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much to take, at most what the order has left to pay. Omit to take what is left to pay. A decimal string with up to 2 decimal places, for example \"12.50\"."},"staff_member_id":{"description":"Who is taking the card. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["sales_order_id","card_reader_id","idempotency_key"],"additionalProperties":false,"examples":[{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","card_reader_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","idempotency_key":"0199a3c4-card-1042"},{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","card_reader_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","amount":"500.00","idempotency_key":"0199a3c4-deposit-1042"}]},"SendPaymentLinkInput":{"type":"object","properties":{"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"contact_point_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer's email address or phone number to send it to (getCustomer, contact_points). Their consent to service messages decides whether it goes."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much to ask for, at most what the order has left to pay: a deposit, say. Omit to ask for whatever is left to pay when the customer pays. A decimal string with up to 2 decimal places, for example \"12.50\"."},"staff_member_id":{"description":"Who is sending it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["sales_order_id","contact_point_id","idempotency_key"],"additionalProperties":false,"examples":[{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","contact_point_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","idempotency_key":"0199a3c4-link-1042"},{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","contact_point_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","amount":"1000.00","idempotency_key":"0199a3c4-deposit-link-1042"}]},"RefundCardInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much to give back to the card: at most amount_refundable, and at most what the order holds for the customer. Omit for all of amount_refundable. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the money was given back."},"staff_member_id":{"description":"Who is giving the refund. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["note","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"250.00","note":"Cover not wanted","idempotency_key":"0199a3c4-refund-1042"},{"note":"Order called off","idempotency_key":"0199a3c4-refund-1043"}]},"CancelCardChargeInput":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why the charge is called off, for the record."}},"additionalProperties":false,"examples":[{},{"reason":"Customer will pay by check"}]},"SyncCardProcessorInput":{"type":"object","properties":{},"additionalProperties":false,"examples":[{}]},"KeepCardOnFileInput":{"type":"object","properties":{"card_reader_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The reader the customer presents the card at (listCardReaders)."},"agreement":{"type":"object","properties":{"subscription_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The subscription whose orders it lets the card be charged for."},"amount_limit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most one cycle's order may be charged. An order that comes to more is not charged: the customer is asked to pay it another way. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancel_instructions":{"type":"string","minLength":1,"maxLength":500,"description":"How the customer stops the charges, in the business's words, for example \"Call the Boise store at 208-555-0100\"."},"evidence_file_id":{"description":"The customer's signature or a signed form, when one was taken (uploadFile).","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["subscription_id","amount_limit","cancel_instructions"],"additionalProperties":false,"description":"What the customer agrees the card may be charged for. Read them the terms (terms_text in the answer) before they present the card."},"staff_member_id":{"description":"Who is setting it up. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["card_reader_id","agreement","idempotency_key"],"additionalProperties":false,"examples":[{"card_reader_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","agreement":{"subscription_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","amount_limit":"150.00","cancel_instructions":"Call the Boise store at 208-555-0100 before the next visit."},"idempotency_key":"0199a3c4-keep-card-77"}]},"RemoveSavedCardInput":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why the card is taken off file."}},"additionalProperties":false,"examples":[{},{"reason":"Customer asked to pay by check from now on"}]},"EndCardAgreementInput":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why it ended."},"ended_by":{"type":"string","enum":["customer","business"],"description":"customer: the customer cancelled it. business: the business ended it, or the card was taken off file."}},"required":["reason","ended_by"],"additionalProperties":false,"examples":[{"reason":"Moved away","ended_by":"customer"}]},"ChargeCardOnFileInput":{"type":"object","properties":{"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["sales_order_id","idempotency_key"],"additionalProperties":false,"examples":[{"sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-cycle-1042"}]},"ChargeDueCardsOnFileInput":{"type":"object","properties":{},"additionalProperties":false,"examples":[{}]},"UpdateCardProcessorInput":{"type":"object","properties":{"deposit_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"fee_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"other_activity_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"Where payout money the processor took for anything but this system's charges posts: a web store's card sales on the same account, hardware, adjustments. NULL until it is chosen. Accepts the record's id or its account code.","x-natural-key":"code"}},"additionalProperties":false,"examples":[{"deposit_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"other_activity_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"}]},"CreateLedgerAccountInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9.-]{0,19}$","description":"The account number or short code, unique within the tenant, for example 1200."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The account name, unique within the tenant, for example \"Inventory\"."},"kind":{"type":"string","enum":["asset","liability","equity","income","cost_of_goods_sold","expense"],"description":"What the account measures. asset, cost_of_goods_sold and expense accounts grow with debits; liability, equity and income accounts grow with credits. Income less cost_of_goods_sold is gross profit. Fixed once the account exists."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What belongs in the account, for whoever (or whatever) chooses accounts for an entry."},{"type":"null"}],"description":"What belongs in the account, for whoever (or whatever) chooses accounts for an entry."},"is_bank_account":{"type":"boolean","description":"True for an account that is a real bank account the business holds money in, as marked by whoever keeps the chart (ledger.post). A store manager brings change from it to the safe and banks takings into it with cash.manage alone; cash from any other account is a manual entry and needs ledger.post. Only an asset account the system does not post to can be one."},"writes_checks":{"type":"boolean","description":"True for the one bank account the business writes its checks on: a refund by check, account credit given back by check, and a gift card cashed out by check all come out of it. Needed only when more than one account is marked as a bank account; with one, checks are written on that one. Set by whoever keeps the chart (ledger.post). Marking one takes the mark off whichever account held it."}},"required":["code","name","kind"],"additionalProperties":false,"examples":[{"code":"1010","name":"Operating Bank","kind":"asset","is_bank_account":true},{"code":"1070","name":"Financing Clearing","kind":"asset","description":"Sales paid by the financing company and not yet funded."}]},"UpdateLedgerAccountInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9.-]{0,19}$","description":"The account number or short code, unique within the tenant, for example 1200."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The account name, unique within the tenant, for example \"Inventory\"."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What belongs in the account, for whoever (or whatever) chooses accounts for an entry."},{"type":"null"}],"description":"What belongs in the account, for whoever (or whatever) chooses accounts for an entry."},"is_active":{"type":"boolean","description":"False once the account is retired: it keeps its history but takes no new entries. An account with a posting_role cannot be retired."},"is_bank_account":{"type":"boolean","description":"True for an account that is a real bank account the business holds money in, as marked by whoever keeps the chart (ledger.post). A store manager brings change from it to the safe and banks takings into it with cash.manage alone; cash from any other account is a manual entry and needs ledger.post. Only an asset account the system does not post to can be one."},"writes_checks":{"type":"boolean","description":"True for the one bank account the business writes its checks on: a refund by check, account credit given back by check, and a gift card cashed out by check all come out of it. Needed only when more than one account is marked as a bank account; with one, checks are written on that one. Set by whoever keeps the chart (ledger.post). Marking one takes the mark off whichever account held it."}},"additionalProperties":false,"examples":[{"name":"Undeposited Funds - Boise"},{"is_active":false}]},"PostJournalEntryInput":{"type":"object","properties":{"entry_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The accounting date the entry belongs to, in the business's own calendar. Reports by period use this, not created_at."},"memo":{"type":"string","minLength":1,"maxLength":1000,"description":"What the entry is for, in words."},"lines":{"minItems":2,"maxItems":100,"type":"array","items":{"type":"object","properties":{"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"debit":{"description":"Amount debited. Zero when the line is a credit. Send debit or credit, not both.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"credit":{"description":"Amount credited. Zero when the line is a debit. Send debit or credit, not both.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"memo":{"type":"string","minLength":1,"maxLength":500,"description":"A note about this line in particular."}},"required":["ledger_account_id"],"additionalProperties":false},"description":"The debits and credits. Debits must add up to the credits."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["entry_date","memo","lines","idempotency_key"],"additionalProperties":false,"examples":[{"entry_date":"2026-09-30","memo":"September card processing fees","lines":[{"ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","debit":"412.18"},{"ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","credit":"412.18"}],"idempotency_key":"0199a3c4-fees-2026-09"}]},"ReverseJournalEntryInput":{"type":"object","properties":{"entry_date":{"description":"The date of the reversal. Defaults to today; not before the entry.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"memo":{"description":"Why it is reversed.","type":"string","minLength":1,"maxLength":1000}},"additionalProperties":false,"examples":[{"memo":"Posted to the wrong account"}]},"CreateRegisterInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"What staff call the register, for example \"Front counter\"."},"code":{"type":"string","pattern":"^[A-Z0-9]{1,8}$","description":"A short code for the register, unique at its location: up to 8 capital letters and digits, for example FRONT or R2."}},"required":["location_id","name","code"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","name":"Front counter","code":"FRONT"}]},"UpdateRegisterInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What staff call the register, for example \"Front counter\"."},"code":{"type":"string","pattern":"^[A-Z0-9]{1,8}$","description":"A short code for the register, unique at its location: up to 8 capital letters and digits, for example FRONT or R2."},"is_active":{"type":"boolean","description":"False once the register is no longer used: no drawer can be opened on it. It cannot be retired with a drawer open."}},"additionalProperties":false,"examples":[{"name":"Service counter"},{"is_active":false}]},"CreateCashPaidReasonInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The reason as staff choose it, unique within the business."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"note_required":{"type":"boolean","description":"True (the default) when each paid-in or paid-out for this reason must say what it was for, for example \"Fuel, truck 2, Chevron receipt\"."},"direction":{"type":"string","enum":["in","out"],"description":"out: cash leaves the drawer (a paid-out). in: cash comes into it (a paid-in). Fixed once the reason exists."}},"required":["name","ledger_account_id","direction"],"additionalProperties":false,"examples":[{"name":"Fuel for delivery truck","direction":"out","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"UpdateCashPaidReasonInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The reason as staff choose it, unique within the business."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"note_required":{"type":"boolean","description":"True (the default) when each paid-in or paid-out for this reason must say what it was for, for example \"Fuel, truck 2, Chevron receipt\"."},"is_active":{"type":"boolean","description":"False once the reason is no longer used: no new cash moves for it."}},"additionalProperties":false,"examples":[{"is_active":false}]},"OpenCashDrawerInput":{"type":"object","properties":{"opening_float":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash put in the drawer to make change, taken out of the location's safe. 0 for a drawer opened empty. A decimal string with up to 2 decimal places, for example \"12.50\"."},"staff_member_id":{"description":"Who opened the drawer and counted the float in. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["opening_float","idempotency_key"],"additionalProperties":false,"examples":[{"opening_float":"200.00","idempotency_key":"0199a3c4-open-front-1003"}]},"PayCashInput":{"type":"object","properties":{"cash_paid_reason_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Why the cash was paid in or out. Set for paid_in and paid_out only."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much cash. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"type":"string","minLength":1,"maxLength":500,"description":"What it was for. Required for a paid-in or paid-out when its reason says so."},"staff_member_id":{"description":"Who put the cash in or took it out. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["cash_paid_reason_id","amount","idempotency_key"],"additionalProperties":false,"examples":[{"cash_paid_reason_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount":"40.00","note":"Fuel, truck 2","idempotency_key":"0199a3c4-fuel-1003"}]},"DropCashInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much cash goes from the drawer into the safe. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"type":"string","minLength":1,"maxLength":500,"description":"What it was for. Required for a paid-in or paid-out when its reason says so."},"staff_member_id":{"description":"Who put the cash in or took it out. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"500.00","idempotency_key":"0199a3c4-drop-1003-1"}]},"CloseCashDrawerInput":{"type":"object","properties":{"counted_cash":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash counted in the drawer when it was closed, all of which goes into the safe. Never changed afterwards: a recount is recounted_cash. A decimal string with up to 2 decimal places, for example \"12.50\"."},"count":{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"denomination":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The value of one bill, coin or roll, for example 20.00, 0.25, or 10.00 for a roll of quarters. A decimal string with up to 2 decimal places, for example \"12.50\"."},"quantity":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many of them were counted."}},"required":["denomination","quantity"],"additionalProperties":false},"description":"The count by denomination: each bill, coin or roll and how many. They add up to the figure counted."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything the person closing wants recorded, for example why it is short."},"staff_member_id":{"description":"Who counted the drawer and closed it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"description":"Optional. A drawer is closed or reconciled once, so sending the same request again is already safe without a key: it answers with the drawer. Accepted so a caller can send one with every cash write.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"additionalProperties":false,"examples":[{"counted_cash":"1245.50"},{"count":[{"denomination":"20.00","quantity":40},{"denomination":"5.00","quantity":9},{"denomination":"0.25","quantity":2}],"note":"Two bills torn, taped"}]},"ReconcileCashDrawerInput":{"type":"object","properties":{"recounted_cash":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What a manager counted when reconciling, if the cash was counted again. A figure other than counted_cash moves the difference into or out of the safe, against Cash Over and Short. NULL when it was not recounted. A decimal string with up to 2 decimal places, for example \"12.50\"."},"count":{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"denomination":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The value of one bill, coin or roll, for example 20.00, 0.25, or 10.00 for a roll of quarters. A decimal string with up to 2 decimal places, for example \"12.50\"."},"quantity":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many of them were counted."}},"required":["denomination","quantity"],"additionalProperties":false},"description":"The count by denomination: each bill, coin or roll and how many. They add up to the figure counted."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"What the manager found: why the drawer was over or short, and what was done. Required to reconcile."},"staff_member_id":{"description":"The manager who reconciled a drawer closed over or short. NULL when it was reconciled at once, within the business's tolerance. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"description":"Optional. A drawer is closed or reconciled once, so sending the same request again is already safe without a key: it answers with the drawer. Accepted so a caller can send one with every cash write.","type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$"}},"required":["note"],"additionalProperties":false,"examples":[{"note":"Short 5.00: change given twice on one sale, per the camera"},{"recounted_cash":"1250.00","note":"Recounted: a 5 stuck to a 20"}]},"FundCashSafeInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much cash goes into the safe. A decimal string with up to 2 decimal places, for example \"12.50\"."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"note":{"type":"string","minLength":1,"maxLength":500,"description":"What it was for, or what a count found."},"staff_member_id":{"description":"Who moved or counted the cash. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","ledger_account_id","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"1000.00","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","note":"Starting cash for the safe","idempotency_key":"0199a3c4-fund-boi-1"}]},"RecordBankDepositInput":{"type":"object","properties":{"cash":{"default":"0.00","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash taken out of the safe for the bank. Zero for a deposit of checks only. A decimal string with up to 2 decimal places, for example \"12.50\"."},"checks":{"default":[],"description":"The checks it carries, from getCashSafe's checks_to_bank. A check is banked once.","maxItems":500,"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["payment","account_payment","gift_card"],"description":"Where the check was recorded, as checks_to_bank gives it."},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The check's id, as checks_to_bank gives it."}},"required":["source","id"],"additionalProperties":false}},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"reference":{"type":"string","minLength":1,"maxLength":100,"description":"The bag or deposit slip number people quote."},"note":{"type":"string","minLength":1,"maxLength":500,"description":"Anything worth knowing about it."},"staff_member_id":{"description":"Who took it to the bank. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["ledger_account_id","idempotency_key"],"additionalProperties":false,"examples":[{"cash":"2400.00","checks":[{"source":"payment","id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}],"ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","reference":"Bag 88213","idempotency_key":"0199a3c4-deposit-boi-1012"}]},"CountCashSafeInput":{"type":"object","properties":{"counted_cash":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For a count, the cash counted in the safe. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"type":"string","minLength":1,"maxLength":500,"description":"What it was for, or what a count found."},"staff_member_id":{"description":"Who moved or counted the cash. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["counted_cash","idempotency_key"],"additionalProperties":false,"examples":[{"counted_cash":"1645.00","idempotency_key":"0199a3c4-count-boi-1012"}]},"CreateBrandInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The brand name, unique within the tenant."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Hot Spring"}]},"UpdateBrandInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The brand name, unique within the tenant."},"is_active":{"type":"boolean","description":"False once the tenant no longer carries the brand. Its items and history stay."}},"additionalProperties":false,"examples":[{"is_active":false}]},"CreateCategoryInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Category name, unique among its siblings."},"parent_category_id":{"default":null,"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category this one sits under. NULL for a top-level category."},"income_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The income account that sales of items in this category post to. NULL uses the tenant's Sales account. An income account. Changing it needs ledger.post as well. Accepts the record's id or its account code.","x-natural-key":"code"},"cost_of_goods_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The cost of goods sold account that the cost of items in this category posts to when they are sold or returned, so margin can be read per product line. NULL uses the tenant's Cost of Goods Sold account. A change applies from then on, to returns as well: goods sold before it and brought back after come back on the new account, as their income does. An account of kind cost_of_goods_sold that the system posts nothing else to. Changing it needs ledger.post as well. Accepts the record's id or its account code.","x-natural-key":"code"},"cancellation_fee_percent":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cancellation fee on this category's items, as a percent of what they sell for before tax, for example 25 on special-order tubs. NULL uses the business's cancellation_fee_percent. Only items filed directly under this category; a subcategory has its own. Changing it needs policy.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cancellation fee on this category's items, as a percent of what they sell for before tax, for example 25 on special-order tubs. NULL uses the business's cancellation_fee_percent. Only items filed directly under this category; a subcategory has its own. Changing it needs policy.manage."},"restocking_fee_percent":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The restocking fee on this category's items when they come back, as a percent of what they sold for before tax. NULL uses the business's restocking_fee_percent. Only items filed directly under this category. Changing it needs policy.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The restocking fee on this category's items when they come back, as a percent of what they sold for before tax. NULL uses the business's restocking_fee_percent. Only items filed directly under this category. Changing it needs policy.manage."},"returnable_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after they were handed over this category's items can come back on their order, for example 30 on chemicals and 0 on custom covers. NULL (the default) takes the business's window (tenant_policy.return_days); 3650 takes them back at any time whatever the business's. A return past it needs order.cancel where the goods come back. Only items filed directly under this category. Changing it needs policy.manage."},"repurchase_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":730},{"type":"null"}],"description":"How many days a customer usually goes before buying from this category again, for example 90 for filters or 30 for a sanitizer. Used to tell when a customer who has bought it only once is due to buy again. NULL (the default): no one is reported as due for it on that basis."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Water Care"},{"name":"Sanitizers","parent_category_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"name":"Hot Tubs","income_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","cost_of_goods_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"name":"Filters","repurchase_days":90}]},"UpdateCategoryInput":{"type":"object","properties":{"parent_category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category this one sits under. NULL for a top-level category."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Category name, unique among its siblings."},"is_active":{"type":"boolean","description":"False once the category is retired. Items already filed under it keep it."},"income_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The income account that sales of items in this category post to. NULL uses the tenant's Sales account. An income account. Changing it needs ledger.post as well. Accepts the record's id or its account code.","x-natural-key":"code"},"cost_of_goods_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The cost of goods sold account that the cost of items in this category posts to when they are sold or returned, so margin can be read per product line. NULL uses the tenant's Cost of Goods Sold account. A change applies from then on, to returns as well: goods sold before it and brought back after come back on the new account, as their income does. An account of kind cost_of_goods_sold that the system posts nothing else to. Changing it needs ledger.post as well. Accepts the record's id or its account code.","x-natural-key":"code"},"cancellation_fee_percent":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cancellation fee on this category's items, as a percent of what they sell for before tax, for example 25 on special-order tubs. NULL uses the business's cancellation_fee_percent. Only items filed directly under this category; a subcategory has its own. Changing it needs policy.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cancellation fee on this category's items, as a percent of what they sell for before tax, for example 25 on special-order tubs. NULL uses the business's cancellation_fee_percent. Only items filed directly under this category; a subcategory has its own. Changing it needs policy.manage."},"restocking_fee_percent":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The restocking fee on this category's items when they come back, as a percent of what they sold for before tax. NULL uses the business's restocking_fee_percent. Only items filed directly under this category. Changing it needs policy.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The restocking fee on this category's items when they come back, as a percent of what they sold for before tax. NULL uses the business's restocking_fee_percent. Only items filed directly under this category. Changing it needs policy.manage."},"returnable_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after they were handed over this category's items can come back on their order, for example 30 on chemicals and 0 on custom covers. NULL (the default) takes the business's window (tenant_policy.return_days); 3650 takes them back at any time whatever the business's. A return past it needs order.cancel where the goods come back. Only items filed directly under this category. Changing it needs policy.manage."},"repurchase_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":730},{"type":"null"}],"description":"How many days a customer usually goes before buying from this category again, for example 90 for filters or 30 for a sanitizer. Used to tell when a customer who has bought it only once is due to buy again. NULL (the default): no one is reported as due for it on that basis."}},"additionalProperties":false,"examples":[{"name":"Spa Chemicals"},{"parent_category_id":null},{"is_active":false},{"cost_of_goods_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"cancellation_fee_percent":"25.00","restocking_fee_percent":"20.00","returnable_days":0},{"repurchase_days":30}]},"CreateItemInput":{"type":"object","properties":{"sku":{"type":"string","maxLength":64,"pattern":"^\\S(.*\\S)?$","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Short name shown on screens, receipts and invoices."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Longer description for quotes and the web store."},{"type":"null"}],"description":"Longer description for quotes and the web store."},"kind":{"type":"string","enum":["good","service"],"description":"good for a physical product, service for labor, fees and anything else with no physical stock. Fixed once the item exists."},"tracking":{"default":"none","type":"string","enum":["none","quantity","serial"],"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},"brand_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The brand the item is sold under."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category the item is filed under."},"model":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The manufacturer's model name, for example \"Grandee\"."},{"type":"null"}],"description":"The manufacturer's model name, for example \"Grandee\"."},"mpn":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"Manufacturer part number."},{"type":"null"}],"description":"Manufacturer part number."},"gtin":{"anyOf":[{"type":"string","pattern":"^\\d{8,14}$"},{"type":"null"}],"description":"The barcode number printed on the product (UPC, EAN or other GTIN), digits only."},"unit_text":{"default":"each","type":"string","minLength":1,"maxLength":20,"description":"The unit one of this item is sold in, for example each, hour or lb. Quantities and the price are per this unit."},"whole_units":{"default":true,"type":"boolean","description":"True when the item is sold, bought, moved and counted only in whole units of unit_text, as a filter cartridge or a cover is: a quantity with a fraction is refused everywhere. False when part of a unit makes sense, as 2.5 lb of a chemical or 1.5 hours of labor. Always true for a serial-tracked item."},"lot_tracked":{"default":false,"type":"boolean","description":"True to record the lot (batch) of everything of this item that comes in and goes out, so a recall can be traced to the customers who bought a lot and the shelves that hold it. Only for an item tracked by quantity. Turning it on for an item with stock puts that stock in its unknown lot. It can be turned off only while no lot with a code has stock."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale."},"tax_class_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"How the item is taxed. NULL uses the tenant's default class."},"vendors":{"default":[],"description":"Who supplies the item and at what cost, each as in setItemVendor.","maxItems":10,"type":"array","items":{"type":"object","properties":{"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor that supplies the item."},"vendor_sku":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The vendor's own code for the item, as it must appear on an order to them."},{"type":"null"}],"description":"The vendor's own code for the item, as it must appear on an order to them."},"cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the vendor currently charges for one unit of the item (the item's unit_text), before freight and tax. Calculated from pack_cost when the vendor quotes by the pack: pack_cost divided by pack_size, to four places. A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor currently charges for one unit of the item (the item's unit_text), before freight and tax. Calculated from pack_cost when the vendor quotes by the pack: pack_cost divided by pack_size, to four places. Sending cost says the vendor quotes per unit, and clears pack_cost."},"pack_text":{"anyOf":[{"type":"string","maxLength":40,"pattern":"^\\S(.*\\S)?$","description":"What the vendor sells the item in, when it is not one unit: case, pallet, drum. Lowercase. NULL when the vendor sells single units."},{"type":"null"}],"description":"What the vendor sells the item in, when it is not one unit: case, pallet, drum. Lowercase. NULL when the vendor sells single units."},"pack_size":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units. null records that the vendor sells single units, and clears the pack."},"pack_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size. A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size."},"is_preferred":{"default":false,"type":"boolean","description":"True for the vendor to order this item from by default. At most one per item."}},"required":["vendor_id"],"additionalProperties":false}},"packs":{"default":[],"description":"Packs the item is also sold in, each as in setItemPack.","maxItems":10,"type":"array","items":{"type":"object","properties":{"pack_text":{"type":"string","maxLength":40,"pattern":"^\\S(.*\\S)?$","description":"What the pack is called, as a person says it: case, bag, box. Lowercase, unique for the item. An order line names the pack by this."},"pack_size":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit (unit_text) are in one pack. A whole number for an item counted in whole units. A decimal string with up to 3 decimal places, for example \"12.500\"."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Selling price of one pack, before tax: a case is often cheaper than six bottles. NULL sells the pack at the item's price times pack_size. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Selling price of one pack, before tax: a case is often cheaper than six bottles. NULL sells the pack at the item's price times pack_size."}},"required":["pack_text","pack_size"],"additionalProperties":false}}},"required":["sku","name","kind"],"additionalProperties":false,"examples":[{"sku":"HS-GRANDEE-2026","name":"Hot Spring Grandee 2026","kind":"good","tracking":"serial","brand_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","model":"Grandee","price":"16999.00","vendors":[{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","vendor_sku":"GRD-26","cost":"10450.0000","is_preferred":true}]},{"sku":"BG-CHLOR-5LB","name":"Chlorinating granules, 5 lb","kind":"good","tracking":"quantity","unit_text":"bottle","price":"54.99","vendors":[{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","pack_text":"case","pack_size":"6","pack_cost":"240.00"}],"packs":[{"pack_text":"case","pack_size":"6","price":"299.00"}]},{"sku":"LABOR-SVC","name":"Service labor","kind":"service","unit_text":"hour","whole_units":false,"price":"125.00"}]},"UpdateItemInput":{"type":"object","properties":{"sku":{"type":"string","maxLength":64,"pattern":"^\\S(.*\\S)?$","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Short name shown on screens, receipts and invoices."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Longer description for quotes and the web store."},{"type":"null"}],"description":"Longer description for quotes and the web store."},"brand_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The brand the item is sold under."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category the item is filed under."},"model":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The manufacturer's model name, for example \"Grandee\"."},{"type":"null"}],"description":"The manufacturer's model name, for example \"Grandee\"."},"mpn":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"Manufacturer part number."},{"type":"null"}],"description":"Manufacturer part number."},"gtin":{"anyOf":[{"type":"string","pattern":"^\\d{8,14}$"},{"type":"null"}],"description":"The barcode number printed on the product (UPC, EAN or other GTIN), digits only."},"unit_text":{"type":"string","minLength":1,"maxLength":20,"description":"The unit one of this item is sold in, for example each, hour or lb. Quantities and the price are per this unit."},"whole_units":{"type":"boolean","description":"True when the item is sold, bought, moved and counted only in whole units of unit_text, as a filter cartridge or a cover is: a quantity with a fraction is refused everywhere. False when part of a unit makes sense, as 2.5 lb of a chemical or 1.5 hours of labor. Always true for a serial-tracked item."},"lot_tracked":{"type":"boolean","description":"True to record the lot (batch) of everything of this item that comes in and goes out, so a recall can be traced to the customers who bought a lot and the shelves that hold it. Only for an item tracked by quantity. Turning it on for an item with stock puts that stock in its unknown lot. It can be turned off only while no lot with a code has stock."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale."},"tax_class_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"How the item is taxed. NULL uses the tenant's default class."},"is_active":{"type":"boolean","description":"False once the item is retired from sale. It stays so past sales, stock history and installed units still resolve."}},"additionalProperties":false,"examples":[{"price":"17499.00"},{"is_active":false}]},"SetItemVendorInput":{"type":"object","properties":{"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor that supplies the item."},"vendor_sku":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The vendor's own code for the item, as it must appear on an order to them."},{"type":"null"}],"description":"The vendor's own code for the item, as it must appear on an order to them."},"cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the vendor currently charges for one unit of the item (the item's unit_text), before freight and tax. Calculated from pack_cost when the vendor quotes by the pack: pack_cost divided by pack_size, to four places. A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor currently charges for one unit of the item (the item's unit_text), before freight and tax. Calculated from pack_cost when the vendor quotes by the pack: pack_cost divided by pack_size, to four places. Sending cost says the vendor quotes per unit, and clears pack_cost."},"pack_text":{"anyOf":[{"type":"string","maxLength":40,"pattern":"^\\S(.*\\S)?$","description":"What the vendor sells the item in, when it is not one unit: case, pallet, drum. Lowercase. NULL when the vendor sells single units."},{"type":"null"}],"description":"What the vendor sells the item in, when it is not one unit: case, pallet, drum. Lowercase. NULL when the vendor sells single units."},"pack_size":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units. null records that the vendor sells single units, and clears the pack."},"pack_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size. A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size."},"is_preferred":{"type":"boolean","description":"True for the vendor to order this item from by default. At most one per item."},"is_active":{"type":"boolean","description":"False once the vendor no longer supplies the item."}},"required":["vendor_id"],"additionalProperties":false,"examples":[{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","cost":"10650.0000"},{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","pack_text":"pallet","pack_size":"40","pack_cost":"333.33"},{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","pack_size":null},{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","vendor_sku":"GRD-26","is_preferred":true},{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","is_active":false}]},"SetItemPackInput":{"type":"object","properties":{"pack_text":{"type":"string","maxLength":40,"pattern":"^\\S(.*\\S)?$","description":"What the pack is called, as a person says it: case, bag, box. Lowercase, unique for the item. An order line names the pack by this."},"pack_size":{"description":"How many of the item's unit (unit_text) are in one pack. A whole number for an item counted in whole units. Required for a new pack.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Selling price of one pack, before tax: a case is often cheaper than six bottles. NULL sells the pack at the item's price times pack_size. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Selling price of one pack, before tax: a case is often cheaper than six bottles. NULL sells the pack at the item's price times pack_size."},"is_active":{"type":"boolean","description":"False once the item is no longer sold in this pack. Lines already sold in it keep it."}},"required":["pack_text"],"additionalProperties":false,"examples":[{"pack_text":"case","pack_size":"6","price":"25.00"},{"pack_text":"bag","pack_size":"25"},{"pack_text":"case","is_active":false}]},"SetItemOptionGroupInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What the choice is called, unique for the model whatever its case. Orders name the group by it."},"position":{"description":"Where the group comes among the model's groups, smallest first. A new group defaults to after the model's others.","type":"integer","minimum":0,"maximum":1000},"is_required":{"description":"True when every unit is built with one of its options, so the customer must choose one (a shell color). False for an extra the customer may leave out (an audio system). Defaults to true for a new group.","type":"boolean"},"is_active":{"description":"False once the factory no longer offers the choice. Units already built with it keep it.","type":"boolean"},"options":{"default":[],"description":"Options to add to the group, or to change by name: the fields sent change, the rest stay. Options not sent are left as they are.","maxItems":100,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What the option is called, unique in its group whatever its case. Orders name the option by it."},"position":{"description":"Where the option comes in its group, smallest first. A new option defaults to after the group's others, in the order sent.","type":"integer","minimum":0,"maximum":1000},"price_difference":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the option adds to the model's selling price, before tax: 0 for one included in the price, negative for one that costs less. Taken onto an order line when the option is chosen, so a later change does not reprice orders already written. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost_difference":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the option adds to what the factory charges for the model, per unit: added to a purchase order line's cost when the option is ordered. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"is_active":{"description":"False once the factory no longer builds it. Units already built with it keep it, and can still be sold.","type":"boolean"}},"required":["name"],"additionalProperties":false}}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Shell color","options":[{"name":"Sterling Marble"},{"name":"Tuscan Sun","price_difference":"250.00","cost_difference":"140.0000"}]},{"name":"Audio system","is_required":false,"options":[{"name":"Bluetooth","price_difference":"899.00"}]},{"name":"Shell color","options":[{"name":"Tuscan Sun","is_active":false}]}]},"CreateSalesChannelInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The channel's name, unique within the tenant, for example \"Web store\"."},"kind":{"type":"string","enum":["store","web","phone","field","other"],"description":"store, web, phone, field or other."},"location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The location that fulfils the channel's orders and whose stock it offers. NULL when orders are assigned a location one by one. Accepts the record's id or its branch code.","x-natural-key":"branch_code"}},"required":["name","kind"],"additionalProperties":false,"examples":[{"name":"Web store","kind":"web","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"UpdateSalesChannelInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The channel's name, unique within the tenant, for example \"Web store\"."},"kind":{"type":"string","enum":["store","web","phone","field","other"],"description":"store, web, phone, field or other."},"location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The location that fulfils the channel's orders and whose stock it offers. NULL when orders are assigned a location one by one. Accepts the record's id or its branch code.","x-natural-key":"branch_code"},"is_active":{"type":"boolean","description":"False once the channel is closed."}},"additionalProperties":false,"examples":[{"is_active":false}]},"SetChannelListingInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"price":{"description":"The price on this channel, when it differs from the item's price. NULL uses the item's price.","anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}]},"is_listed":{"default":true,"description":"False to take the item off the channel without losing the listing.","type":"boolean"}},"required":["item_id"],"additionalProperties":false,"examples":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","price":"49.99"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","is_listed":false}]},"CreateCommissionPlanInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The plan's name, unique within the tenant."},"basis":{"type":"string","enum":["revenue","gross_profit"],"description":"What the rate is applied to: revenue (the selling price before tax) or gross_profit (revenue less what the goods cost)."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction: 0.05 is five percent. A category can have its own rate in commission_plan_rate. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"earned_when":{"default":"hand_over","type":"string","enum":["hand_over","paid_in_full"],"description":"When commission counts as earned: hand_over (on the day the goods are handed over) or paid_in_full (held until the order is paid for everything handed over, then earned on the day it is; held again if a payment fails, until the order is paid again). A change applies to goods handed over from then on."},"clawback_days":{"default":null,"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after a hand-over a return, or a failed payment on a paid_in_full plan, still takes its earned commission back, up to 3650. NULL: always. 0: only on the day of the hand-over. Held commission on returned goods is cancelled whatever this says. A change applies to goods handed over from then on."},"floor_at_zero":{"default":false,"type":"boolean","description":"True: commission on a hand-over is never below zero, so goods sold below cost on a gross_profit plan earn nothing rather than costing the salesperson. False: they earn negative commission. A change applies to goods handed over from then on."},"category_rates":{"default":[],"description":"Rates for categories that differ from the plan's own.","maxItems":50,"type":"array","items":{"type":"object","properties":{"item_category_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The category the rate is for."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction, applied to the plan's basis. A decimal string with up to 5 decimal places, for example \"12.50000\"."}},"required":["item_category_id","rate"],"additionalProperties":false}}},"required":["name","basis","rate"],"additionalProperties":false,"examples":[{"name":"Showroom sales","basis":"gross_profit","rate":"0.2","category_rates":[{"item_category_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","rate":"0.1"}]},{"name":"Financed spas","basis":"gross_profit","rate":"0.25","earned_when":"paid_in_full","clawback_days":90,"floor_at_zero":true}]},"UpdateCommissionPlanInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The plan's name, unique within the tenant."},"basis":{"type":"string","enum":["revenue","gross_profit"],"description":"What the rate is applied to: revenue (the selling price before tax) or gross_profit (revenue less what the goods cost)."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction: 0.05 is five percent. A category can have its own rate in commission_plan_rate. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"earned_when":{"type":"string","enum":["hand_over","paid_in_full"],"description":"When commission counts as earned: hand_over (on the day the goods are handed over) or paid_in_full (held until the order is paid for everything handed over, then earned on the day it is; held again if a payment fails, until the order is paid again). A change applies to goods handed over from then on."},"clawback_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after a hand-over a return, or a failed payment on a paid_in_full plan, still takes its earned commission back, up to 3650. NULL: always. 0: only on the day of the hand-over. Held commission on returned goods is cancelled whatever this says. A change applies to goods handed over from then on."},"floor_at_zero":{"type":"boolean","description":"True: commission on a hand-over is never below zero, so goods sold below cost on a gross_profit plan earn nothing rather than costing the salesperson. False: they earn negative commission. A change applies to goods handed over from then on."},"is_active":{"type":"boolean","description":"False once the plan is retired. A retired plan earns nothing new."},"category_rates":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_category_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The category the rate is for."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction, applied to the plan's basis. A decimal string with up to 5 decimal places, for example \"12.50000\"."}},"required":["item_category_id","rate"],"additionalProperties":false},"description":"Category rates to set. A category not sent keeps its rate; send rate 0 for one that should pay nothing."}},"additionalProperties":false,"examples":[{"rate":"0.22"},{"category_rates":[{"item_category_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","rate":"0"}]},{"earned_when":"paid_in_full","clawback_days":60}]},"CreateSalesQuotaInput":{"type":"object","properties":{"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Whose target it is. NULL for a target that belongs to a whole location."},"location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The location the target is for. NULL for a person's target across all locations. Accepts the record's id or its branch code.","x-natural-key":"branch_code"},"period_start":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First business date of the period."},"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last business date of the period, inclusive."},"measure":{"type":"string","enum":["revenue","gross_profit","units"],"description":"What is counted: revenue (sales before tax), gross_profit (revenue less cost) or units (quantity handed over). A person's attainment counts their credit share of each sale."},"target":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The amount or number of units to reach in the period. A decimal string with up to 2 decimal places, for example \"12.50\"."},"item_category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Count only items filed directly in this category. NULL counts everything."}},"required":["period_start","period_end","measure","target"],"additionalProperties":false,"examples":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","period_start":"2026-10-01","period_end":"2026-10-31","measure":"revenue","target":"60000"},{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","period_start":"2026-10-01","period_end":"2026-12-31","measure":"units","target":"12","item_category_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"}]},"UpdateSalesQuotaInput":{"type":"object","properties":{"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Whose target it is. NULL for a target that belongs to a whole location."},"location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The location the target is for. NULL for a person's target across all locations. Accepts the record's id or its branch code.","x-natural-key":"branch_code"},"period_start":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First business date of the period."},"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last business date of the period, inclusive."},"measure":{"type":"string","enum":["revenue","gross_profit","units"],"description":"What is counted: revenue (sales before tax), gross_profit (revenue less cost) or units (quantity handed over). A person's attainment counts their credit share of each sale."},"target":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The amount or number of units to reach in the period. A decimal string with up to 2 decimal places, for example \"12.50\"."},"item_category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Count only items filed directly in this category. NULL counts everything."}},"additionalProperties":false,"examples":[{"target":"65000"}]},"ScheduleAppointmentInput":{"type":"object","properties":{"kind":{"type":"string","enum":["delivery","service","consultation"],"description":"delivery: goods are brought and installed. service: a technician works on equipment. consultation: a meeting to advise or assess, with nothing delivered; one naming a sales_order_id is a site check before that order's delivery."},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"site_id":{"description":"Where the visit is: one of the customer's own sites. NULL when the customer comes to the location.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"work_order_id":{"type":"string","x-natural-key":"work_order_number","description":"Accepts the record's id or its work order number (as a number or a string of digits)."},"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"subscription_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Start of the window the customer was given. ISO 8601 with an offset."},"scheduled_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"End of the window the customer was given. ISO 8601 with an offset."},"instructions":{"type":"string","minLength":1,"maxLength":5000,"description":"What the crew needs to know beforehand: access, equipment to bring, what was promised."},"crew":{"default":[],"description":"Who is going. Mark one is_lead: a delivery is handed over by its lead.","maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is going."},"is_lead":{"default":false,"description":"True for the person in charge of the visit: at most one. A completed delivery is recorded as handed over by the lead.","type":"boolean"}},"required":["staff_member_id"],"additionalProperties":false}},"lines":{"default":[],"description":"For a delivery: the order lines to hand over, and how many of each.","maxItems":100,"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line being delivered."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are being delivered on this visit. 0 takes the line off the delivery. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["sales_order_line_id","quantity"],"additionalProperties":false}}},"required":["kind","customer_id","location_id","scheduled_start","scheduled_end"],"additionalProperties":false,"examples":[{"kind":"delivery","customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","scheduled_start":"2026-10-14T08:00:00-06:00","scheduled_end":"2026-10-14T12:00:00-06:00","instructions":"Crane booked for 9. Customer wants it on the north deck.","crew":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","is_lead":true}],"lines":[{"sales_order_line_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7f","quantity":"1"}]}]},"UpdateAppointmentInput":{"type":"object","properties":{"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Start of the window the customer was given."},"scheduled_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"End of the window the customer was given."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the visit is: one of the customer's own sites. NULL when the customer comes to the location."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"instructions":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What the crew needs to know beforehand: access, equipment to bring, what was promised."},{"type":"null"}],"description":"What the crew needs to know beforehand: access, equipment to bring, what was promised."},"outcome":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What happened, written afterwards."},{"type":"null"}],"description":"What happened, written afterwards."},"subscription_order_id":{"anyOf":[{"type":"string","x-natural-key":"order_number"},{"type":"null"}],"description":"For a care-plan visit: the order of the plan's cycle it carries out (a sales order made by a service-visit subscription, still open when named). Completing the visit hands over that order's services still to hand over, which is when the plan's visit counts as sold; the visit's own time is covered by the plan and charges nothing, unless it is spent on a job the visit also names. Changeable while the visit is scheduled. NULL for any other visit. Accepts the record's id or its order number (as a number or a string of digits).","x-natural-key":"order_number"}},"additionalProperties":false,"examples":[{"scheduled_start":"2026-10-15T08:00:00-06:00","scheduled_end":"2026-10-15T12:00:00-06:00"}]},"SetAppointmentCrewInput":{"type":"object","properties":{"crew":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is going."},"is_lead":{"default":false,"description":"True for the person in charge of the visit: at most one. A completed delivery is recorded as handed over by the lead.","type":"boolean"}},"required":["staff_member_id"],"additionalProperties":false},"description":"Everyone going. Replaces the crew: anyone left out is taken off."}},"required":["crew"],"additionalProperties":false,"examples":[{"crew":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","is_lead":true},{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"}]}]},"SetDeliveryLinesInput":{"type":"object","properties":{"lines":{"maxItems":100,"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line being delivered."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are being delivered on this visit. 0 takes the line off the delivery. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["sales_order_line_id","quantity"],"additionalProperties":false},"description":"Everything to hand over on this delivery. Replaces the list: a line left out comes off."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"sales_order_line_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"1"}]}]},"FinishAppointmentInput":{"type":"object","properties":{"outcome":{"type":"string","minLength":1,"maxLength":5000,"description":"What happened, written afterwards."}},"additionalProperties":false,"examples":[{"outcome":"Set on the north deck, filled and running. Customer walked through care."}]},"MarkAppointmentEnRouteInput":{"type":"object","properties":{"eta":{"description":"When the crew expect to arrive, as told to the customer. ISO 8601 with an offset.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"override_reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the crew goes to a site not shown ready, when the business requires readiness (dispatch_requires_ready_site). Needs delivery.override where the visit is booked and where its orders were sold. Recorded on the visit (site_override_reason) with who gave it."}},"additionalProperties":false,"examples":[{"eta":"2026-10-14T08:40:00-06:00"},{}]},"RecordAppointmentArrivalInput":{"type":"object","properties":{"arrived_at":{"description":"When the crew arrived, by their device. Set once, while it is scheduled, and not before en_route_at. Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"arrival_notes":{"type":"string","minLength":1,"maxLength":5000,"description":"What the crew found on arrival: the unit's condition before unloading, the site as found."},"photos":{"default":[],"description":"Photos of what the crew found: the unit before unloading, the site as found. Upload each with uploadFile first.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."},"taken_at":{"description":"When it was taken, by the device: it may be before it was uploaded, never later than now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken, by the device: latitude in degrees. Sent together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."}},"required":["file_id"],"additionalProperties":false}}},"additionalProperties":false,"examples":[{"arrival_notes":"Unit wrapped and undamaged. Gate open, pad clear.","photos":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","caption":"Unit on the trailer before unloading"}]}]},"AddAppointmentPhotosInput":{"type":"object","properties":{"photos":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."},"taken_at":{"description":"When it was taken, by the device: it may be before it was uploaded, never later than now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken, by the device: latitude in degrees. Sent together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."}},"required":["file_id"],"additionalProperties":false},"description":"The files to attach, each uploaded with uploadFile first. Attaching a file the visit already has does nothing."},"kind":{"default":"photo","description":"photo for images, document for a PDF such as an HOA approval.","type":"string","enum":["photo","document"]}},"required":["photos"],"additionalProperties":false,"examples":[{"photos":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","caption":"Unit placed on the pad"}]}]},"CreateChecklistInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the checklist is called, unique within the business whatever its case."},"purpose":{"type":"string","enum":["site_readiness","delivery","service"],"description":"site_readiness: answered by a site check before a delivery (in person or from the customer's photos). delivery: answered by the crew when they complete a delivery. service: answered by the technician on a job. Fixed once the checklist exists."},"description":{"type":"string","minLength":1,"maxLength":2000,"description":"What the checklist is for, in words."},"items":{"default":[],"description":"The questions, in order.","maxItems":100,"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":500,"description":"The question as the person answering reads it, for example \"Concrete pad poured and cured 7 days or more?\"."},"help_text":{"type":"string","minLength":1,"maxLength":2000,"description":"How to check, for example \"Measure the narrowest point between the street and the pad, gates open.\""},"answer_kind":{"type":"string","enum":["yes_no","number","text","photo"],"description":"yes_no, number, text, or photo (answered by attaching photos, at least min_photos of them)."},"unit_text":{"type":"string","minLength":1,"maxLength":40,"description":"For a number: its unit, for example \"in\" or \"amps\"."},"min_value":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"For a number: the smallest answer that passes, inclusive. NULL for no lower bound. A decimal string with up to 3 decimal places, for example \"12.500\"."},"max_value":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"For a number: the largest answer that passes, inclusive. NULL for no upper bound. A decimal string with up to 3 decimal places, for example \"12.500\"."},"required_answer":{"description":"For a yes/no question: the answer that passes. NULL when the answer is information only and either passes, for example \"Crane needed?\".","type":"boolean"},"min_photos":{"description":"For a photo question: how many photos it needs at least. Ignored for other kinds.","type":"integer","minimum":1,"maximum":40},"is_required":{"default":true,"description":"True when it must be answered: a check that leaves it unanswered can only be recorded as failed.","type":"boolean"},"requires_party_role":{"description":"For a yes/no question: answered yes on a site check, the delivery needs an outside party in this role (a crane) booked and confirmed before the site counts as ready.","type":"string","enum":["crane","electrician","contractor","inspector","other"]}},"required":["prompt","answer_kind"],"additionalProperties":false,"description":"One question, added at the end."}}},"required":["name","purpose"],"additionalProperties":false,"examples":[{"name":"Hot tub site readiness","purpose":"site_readiness","items":[{"prompt":"Concrete pad poured and cured 7 days or more?","answer_kind":"yes_no","required_answer":true},{"prompt":"Photo of the pad","answer_kind":"photo","min_photos":1},{"prompt":"Breaker size","answer_kind":"number","unit_text":"amps","min_value":"40","max_value":"60"},{"prompt":"Crane needed?","answer_kind":"yes_no","requires_party_role":"crane"}]}]},"UpdateChecklistInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the checklist is called, unique within the business whatever its case."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"What the checklist is for, in words."},{"type":"null"}],"description":"What the checklist is for, in words."},"is_active":{"type":"boolean","description":"False once the business no longer uses it. Checks already answered on it keep it."},"add_items":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":500,"description":"The question as the person answering reads it, for example \"Concrete pad poured and cured 7 days or more?\"."},"help_text":{"type":"string","minLength":1,"maxLength":2000,"description":"How to check, for example \"Measure the narrowest point between the street and the pad, gates open.\""},"answer_kind":{"type":"string","enum":["yes_no","number","text","photo"],"description":"yes_no, number, text, or photo (answered by attaching photos, at least min_photos of them)."},"unit_text":{"type":"string","minLength":1,"maxLength":40,"description":"For a number: its unit, for example \"in\" or \"amps\"."},"min_value":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"For a number: the smallest answer that passes, inclusive. NULL for no lower bound. A decimal string with up to 3 decimal places, for example \"12.500\"."},"max_value":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"For a number: the largest answer that passes, inclusive. NULL for no upper bound. A decimal string with up to 3 decimal places, for example \"12.500\"."},"required_answer":{"description":"For a yes/no question: the answer that passes. NULL when the answer is information only and either passes, for example \"Crane needed?\".","type":"boolean"},"min_photos":{"description":"For a photo question: how many photos it needs at least. Ignored for other kinds.","type":"integer","minimum":1,"maximum":40},"is_required":{"default":true,"description":"True when it must be answered: a check that leaves it unanswered can only be recorded as failed.","type":"boolean"},"requires_party_role":{"description":"For a yes/no question: answered yes on a site check, the delivery needs an outside party in this role (a crane) booked and confirmed before the site counts as ready.","type":"string","enum":["crane","electrician","contractor","inspector","other"]}},"required":["prompt","answer_kind"],"additionalProperties":false,"description":"One question, added at the end."},"description":"Questions to add after those it has."}},"additionalProperties":false,"examples":[{"add_items":[{"prompt":"HOA approval","answer_kind":"photo","is_required":false}]},{"is_active":false}]},"UpdateChecklistItemInput":{"type":"object","properties":{"position":{"type":"integer","minimum":1,"maximum":1000,"description":"Where it comes on the checklist, smallest first."},"prompt":{"type":"string","minLength":1,"maxLength":500,"description":"The question as the person answering reads it, for example \"Concrete pad poured and cured 7 days or more?\"."},"help_text":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"How to check, for example \"Measure the narrowest point between the street and the pad, gates open.\""},{"type":"null"}],"description":"How to check, for example \"Measure the narrowest point between the street and the pad, gates open.\""},"unit_text":{"anyOf":[{"type":"string","minLength":1,"maxLength":40,"description":"For a number: its unit, for example \"in\" or \"amps\"."},{"type":"null"}],"description":"For a number: its unit, for example \"in\" or \"amps\"."},"min_value":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"For a number: the smallest answer that passes, inclusive. NULL for no lower bound."},"max_value":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"For a number: the largest answer that passes, inclusive. NULL for no upper bound."},"required_answer":{"description":"For a yes/no question: the answer that passes. NULL when the answer is information only and either passes, for example \"Crane needed?\".","type":["boolean","null"]},"min_photos":{"type":"integer","minimum":1,"maximum":40,"description":"For a photo question: how many photos it needs at least. Ignored for other kinds."},"is_required":{"type":"boolean","description":"True when it must be answered: a check that leaves it unanswered can only be recorded as failed."},"requires_party_role":{"anyOf":[{"type":"string","enum":["crane","electrician","contractor","inspector","other"]},{"type":"null"}],"description":"For a yes/no question: answered yes on a site check, the delivery needs an outside party in this role (a crane) booked and confirmed before the site counts as ready."},"is_active":{"type":"boolean","description":"False once the question is no longer asked. Answers already given keep it."}},"additionalProperties":false,"examples":[{"help_text":"Measure the narrowest point, gates open."},{"is_active":false}]},"RecordTaxExemptionCertificateInput":{"type":"object","properties":{"certificate_number":{"type":"string","minLength":1,"maxLength":100,"description":"The number on the certificate, or the buyer's permit or exemption number it cites, as an auditor would look it up. Fixed."},"reason":{"type":"string","enum":["resale","contractor","nonprofit","government","agricultural","manufacturing","other"],"description":"Why the customer buys without tax: resale (they sell it on), contractor (goods for an exempt job or that a contractor may buy exempt), nonprofit, government, agricultural, manufacturing (used up in making goods), or other (say what in notes). Fixed."},"issuing_region":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$","description":"The state it is good in, as a two-letter code (ID). A sale is covered only when it is taxed in that state: where it is delivered, or the store it is carried out of. NULL for a certificate good in any state, such as a multistate form. Fixed."},{"type":"null"}],"description":"The state it is good in, as a two-letter code (ID). A sale is covered only when it is taxed in that state: where it is delivered, or the store it is carried out of. NULL for a certificate good in any state, such as a multistate form. Fixed. Send null only for a form good in every state."},"effective_on":{"description":"The first day it covers. Fixed. Defaults to today.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"expires_on":{"default":null,"description":"The last day it covers. NULL when it does not expire (many states' resale certificates stay good until withdrawn). Fixed.","anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}]},"file_id":{"description":"The scan or photo of the signed certificate (uploadFile), a PDF or an image. Can be added later.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"Anything an auditor or a manager would want: the job it is for, what other means."}},"required":["certificate_number","reason","issuing_region"],"additionalProperties":false,"examples":[{"certificate_number":"ST-101 / 000123456","reason":"resale","issuing_region":"ID","expires_on":null},{"certificate_number":"EX-2026-0451","reason":"nonprofit","issuing_region":"ID","effective_on":"2026-01-01","expires_on":"2026-12-31","file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"UpdateTaxExemptionCertificateInput":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The scan or photo of the signed certificate (uploadFile), a PDF or an image. Can be added later."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"Anything an auditor or a manager would want: the job it is for, what other means."},{"type":"null"}],"description":"Anything an auditor or a manager would want: the job it is for, what other means."},"is_active":{"type":"boolean","const":false,"description":"Send false to withdraw it as recorded in error. Refused once a sale made untaxed on it has had goods handed over or money taken."}},"additionalProperties":false,"examples":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"is_active":false}]},"AddContactPointInput":{"type":"object","properties":{"kind":{"type":"string","enum":["email","telephone"],"description":"email or telephone."},"value":{"type":"string","minLength":3,"maxLength":320,"description":"The address or number itself: a lowercase email address, or a phone number in E.164 form such as +12085550100."},"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\"."},{"type":"null"}],"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\"."},"is_primary":{"default":false,"type":"boolean","description":"True for the one to use first for its kind. At most one primary email and one primary telephone per customer."},"service_consent":{"default":"unknown","type":"string","enum":["unknown","granted","denied"],"description":"Whether messages about the customer's own orders, deliveries and service may be sent here: unknown, granted or denied."},"marketing_consent":{"default":"unknown","type":"string","enum":["unknown","granted","denied"],"description":"Whether promotional messages may be sent here: unknown, granted or denied. Send nothing promotional unless granted."},"consent_source":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\"."},{"type":"null"}],"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\"."}},"required":["kind","value"],"additionalProperties":false,"examples":[{"kind":"telephone","value":"+12085550100","name":"Pat, mobile","is_primary":true,"service_consent":"granted"},{"kind":"email","value":"pat@example.com","marketing_consent":"granted","consent_source":"Signed up at the Boise home show"}]},"UpdateContactPointInput":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\"."},{"type":"null"}],"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\"."},"is_primary":{"type":"boolean","description":"True for the one to use first for its kind. At most one primary email and one primary telephone per customer."},"service_consent":{"type":"string","enum":["unknown","granted","denied"],"description":"Whether messages about the customer's own orders, deliveries and service may be sent here: unknown, granted or denied."},"marketing_consent":{"type":"string","enum":["unknown","granted","denied"],"description":"Whether promotional messages may be sent here: unknown, granted or denied. Send nothing promotional unless granted."},"consent_source":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\"."},{"type":"null"}],"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\"."},"is_active":{"type":"boolean","description":"False once the address or number is known to be dead or no longer the customer's."},"undeliverable_at":{"type":"null","description":"Send null to clear the mark the email or text service set, once the address is corrected or confirmed."}},"additionalProperties":false,"examples":[{"marketing_consent":"denied","consent_source":"Replied STOP to a text"},{"is_primary":true},{"is_active":false},{"undeliverable_at":null}]},"CreateSiteInput":{"type":"object","properties":{"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"name":{"default":"Home","type":"string","minLength":1,"maxLength":200,"description":"What the customer calls the place, for example \"Home\" or \"McCall cabin\"."},"street_address":{"type":"string","minLength":1,"maxLength":500,"description":"Street address. A second line (unit, lot) follows a line break."},"address_locality":{"type":"string","minLength":1,"maxLength":200,"description":"City or town."},"address_region":{"type":"string","minLength":1,"maxLength":200,"description":"State or province code, for example ID."},"postal_code":{"type":"string","minLength":1,"maxLength":20,"description":"ZIP or postal code."},"address_country":{"default":"US","type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"latitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Latitude in decimal degrees, for routing. Set together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Latitude in decimal degrees, for routing. Set together with longitude."},"longitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Longitude in decimal degrees, for routing. Set together with latitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Longitude in decimal degrees, for routing. Set together with latitude."},"access_notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed."},{"type":"null"}],"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the site is in. Sales delivered here are taxed by it. NULL falls back to the selling location's area."}},"required":["customer_id","street_address","address_locality","address_region","postal_code"],"additionalProperties":false,"examples":[{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","name":"McCall cabin","street_address":"12 Lakeview Rd","address_locality":"McCall","address_region":"ID","postal_code":"83638","access_notes":"Gravel drive, crane needed. Tub goes on the north deck."}]},"UpdateSiteInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the customer calls the place, for example \"Home\" or \"McCall cabin\"."},"street_address":{"type":"string","minLength":1,"maxLength":500,"description":"Street address. A second line (unit, lot) follows a line break."},"address_locality":{"type":"string","minLength":1,"maxLength":200,"description":"City or town."},"address_region":{"type":"string","minLength":1,"maxLength":200,"description":"State or province code, for example ID."},"postal_code":{"type":"string","minLength":1,"maxLength":20,"description":"ZIP or postal code."},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"latitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Latitude in decimal degrees, for routing. Set together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Latitude in decimal degrees, for routing. Set together with longitude."},"longitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Longitude in decimal degrees, for routing. Set together with latitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Longitude in decimal degrees, for routing. Set together with latitude."},"access_notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed."},{"type":"null"}],"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the site is in. Sales delivered here are taxed by it. NULL falls back to the selling location's area."},"is_active":{"type":"boolean","description":"False once the customer no longer has the place. Its history stays."}},"additionalProperties":false,"examples":[{"access_notes":"Gate code 4471. Dog is friendly."},{"is_active":false}]},"CreateCustomerInput":{"type":"object","properties":{"kind":{"default":"person","type":"string","enum":["person","organization"],"description":"person for an individual or household, organization for a business or other body."},"name":{"description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization. For a person it defaults to given_name and family_name.","type":"string","minLength":1,"maxLength":200},"given_name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"First name. Only for a person."},{"type":"null"}],"description":"First name. Only for a person."},"family_name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Last name, used for sorting and lookup. Only for a person."},{"type":"null"}],"description":"Last name, used for sorting and lookup. Only for a person."},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Billing street address. A second line (suite, unit) follows a line break."},{"type":"null"}],"description":"Billing street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Billing city or town."},{"type":"null"}],"description":"Billing city or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Billing state or province code, for example ID."},{"type":"null"}],"description":"Billing state or province code, for example ID."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":20,"description":"Billing ZIP or postal code."},{"type":"null"}],"description":"Billing ZIP or postal code."},"address_country":{"default":"US","type":"string","pattern":"^[A-Z]{2}$","description":"Billing country as an ISO 3166-1 alpha-2 code."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff. Not shown to the customer."},{"type":"null"}],"description":"Free-form notes for staff. Not shown to the customer."},"credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over."},"payment_terms_days":{"description":"How many days after goods are handed over payment falls due. 0 is due on hand-over; 30 is \"net 30\". Defaults to the business's terms for new customers (new_customer_payment_terms_days in getPolicy); longer terms need credit.manage.","type":"integer","minimum":0,"maximum":365},"is_tax_exempt":{"default":false,"description":"True when the customer buys without sales tax, for example a reseller, a contractor or a tax-exempt organization. Their sales are untaxed on a tax_exemption_certificate that covers the sale's day and state; when none does, tenant_policy.exemption_without_certificate says what happens. Recording a certificate sets it. Needs tax.override. Record their certificate with recordTaxExemptionCertificate instead, which does both.","type":"boolean"},"contact_points":{"default":[],"description":"Email addresses and phone numbers to record with the customer, each as in addContactPoint.","maxItems":20,"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["email","telephone"],"description":"email or telephone."},"value":{"type":"string","minLength":3,"maxLength":320,"description":"The address or number itself: a lowercase email address, or a phone number in E.164 form such as +12085550100."},"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\"."},{"type":"null"}],"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\"."},"is_primary":{"default":false,"type":"boolean","description":"True for the one to use first for its kind. At most one primary email and one primary telephone per customer."},"service_consent":{"default":"unknown","type":"string","enum":["unknown","granted","denied"],"description":"Whether messages about the customer's own orders, deliveries and service may be sent here: unknown, granted or denied."},"marketing_consent":{"default":"unknown","type":"string","enum":["unknown","granted","denied"],"description":"Whether promotional messages may be sent here: unknown, granted or denied. Send nothing promotional unless granted."},"consent_source":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\"."},{"type":"null"}],"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\"."}},"required":["kind","value"],"additionalProperties":false}},"sites":{"default":[],"description":"Places the customer takes delivery or has equipment, each as in createSite.","maxItems":20,"type":"array","items":{"type":"object","properties":{"name":{"default":"Home","type":"string","minLength":1,"maxLength":200,"description":"What the customer calls the place, for example \"Home\" or \"McCall cabin\"."},"street_address":{"type":"string","minLength":1,"maxLength":500,"description":"Street address. A second line (unit, lot) follows a line break."},"address_locality":{"type":"string","minLength":1,"maxLength":200,"description":"City or town."},"address_region":{"type":"string","minLength":1,"maxLength":200,"description":"State or province code, for example ID."},"postal_code":{"type":"string","minLength":1,"maxLength":20,"description":"ZIP or postal code."},"address_country":{"default":"US","type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"latitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Latitude in decimal degrees, for routing. Set together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Latitude in decimal degrees, for routing. Set together with longitude."},"longitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Longitude in decimal degrees, for routing. Set together with latitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Longitude in decimal degrees, for routing. Set together with latitude."},"access_notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed."},{"type":"null"}],"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the site is in. Sales delivered here are taxed by it. NULL falls back to the selling location's area."}},"required":["street_address","address_locality","address_region","postal_code"],"additionalProperties":false}}},"additionalProperties":false,"examples":[{"given_name":"Pat","family_name":"Lee","contact_points":[{"kind":"telephone","value":"+12085550100","is_primary":true,"service_consent":"granted"}],"sites":[{"street_address":"415 W Bannock St","address_locality":"Boise","address_region":"ID","postal_code":"83702"}]},{"kind":"organization","name":"Sun Valley Lodge","payment_terms_days":30,"credit_limit":"25000.00","contact_points":[{"kind":"email","value":"ap@sunvalleylodge.example","name":"Accounts payable"}]}]},"UpdateCustomerInput":{"type":"object","properties":{"kind":{"type":"string","enum":["person","organization"],"description":"person for an individual or household, organization for a business or other body."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"given_name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"First name. Only for a person."},{"type":"null"}],"description":"First name. Only for a person."},"family_name":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Last name, used for sorting and lookup. Only for a person."},{"type":"null"}],"description":"Last name, used for sorting and lookup. Only for a person."},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Billing street address. A second line (suite, unit) follows a line break."},{"type":"null"}],"description":"Billing street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Billing city or town."},{"type":"null"}],"description":"Billing city or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Billing state or province code, for example ID."},{"type":"null"}],"description":"Billing state or province code, for example ID."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":20,"description":"Billing ZIP or postal code."},{"type":"null"}],"description":"Billing ZIP or postal code."},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"Billing country as an ISO 3166-1 alpha-2 code."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff. Not shown to the customer."},{"type":"null"}],"description":"Free-form notes for staff. Not shown to the customer."},"credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over."},"payment_terms_days":{"type":"integer","minimum":0,"maximum":365,"description":"How many days after goods are handed over payment falls due. 0 is due on hand-over; 30 is \"net 30\"."},"overdue_hold_released_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day the customer may have goods on account despite something overdue, when the tenant holds overdue accounts (tenant_policy.hold_when_overdue_days). NULL releases nothing. Setting it later needs credit.manage. The credit limit still applies."},"is_tax_exempt":{"type":"boolean","description":"True when the customer buys without sales tax, for example a reseller, a contractor or a tax-exempt organization. Their sales are untaxed on a tax_exemption_certificate that covers the sale's day and state; when none does, tenant_policy.exemption_without_certificate says what happens. Recording a certificate sets it."},"is_active":{"type":"boolean","description":"False for a customer the tenant no longer deals with. Their history stays."}},"additionalProperties":false,"examples":[{"credit_limit":"10000.00","payment_terms_days":30},{"is_tax_exempt":false},{"overdue_hold_released_until":"2026-10-31"},{"is_active":false}]},"EnrollDeviceInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What staff call this counter computer, for example \"Boise front counter\". Names the device and every session switched into on it."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"register_id":{"default":null,"description":"For a device key: the register (cash drawer) the counter computer takes cash into, at its store. NULL for none, and for every other key. Fixed once the key exists.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]},"card_reader_id":{"default":null,"description":"For a device key: the card reader the counter computer sends charges to. NULL for a separate card terminal, and for every other key. Fixed once the key exists.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]},"expires_in_days":{"default":365,"description":"How many days, on the business's clock, until the device stops working and is enrolled again: 1 to 365, 365 by default.","type":"integer","minimum":1,"maximum":365},"password":{"type":"string","minLength":1,"maxLength":256,"description":"The password of the person enrolling it, entered again, so a session left open cannot enroll a counter."}},"required":["name","location_id","password"],"additionalProperties":false,"examples":[{"name":"Boise front counter","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","register_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","password":"correct horse battery"}]},"SwitchStaffInput":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The person switching in, as listDeviceStaff gives them."},"pin":{"type":"string","pattern":"^[0-9]{4,8}$","description":"Their PIN."},"approval":{"default":false,"description":"True when a manager switches in only to approve one action (a refund, a paid-out) for whoever is at the counter: that person stays switched in, and the session returned lasts two minutes at most. Do the one action with it, then sign it out (signOut). False by default: the person takes over the counter.","type":"boolean"}},"required":["staff_member_id","pin"],"additionalProperties":false,"examples":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","pin":"482913"}]},"SetPinInput":{"type":"object","properties":{"password":{"type":"string","minLength":1,"maxLength":256,"description":"Your password, entered again, so a session left open cannot set a PIN."},"new_pin":{"type":"string","pattern":"^[0-9]{4,8}$","description":"The new PIN: as many digits as the business's pin_length (6 by default), not one digit repeated or a run like 123456. Keep it to yourself: with it, anyone at a counter can act as you."}},"required":["password","new_pin"],"additionalProperties":false,"examples":[{"password":"correct horse battery","new_pin":"482913"}]},"RecordSiteCheckInput":{"type":"object","properties":{"site_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer's site checked."},"sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"method":{"type":"string","enum":["on_site","remote"],"description":"on_site: someone was there. remote: judged from photos or a call with the customer."},"appointment_id":{"description":"The site visit it was done on, if any.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"checklist_id":{"description":"The checklist answered. Defaults to the business's site readiness checklist (getPolicy).","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"staff_member_id":{"description":"Who checked. Defaults to the caller's.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"answers":{"default":[],"description":"The answers, one per question; each question's photos with it.","maxItems":100,"type":"array","items":{"type":"object","properties":{"checklist_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The question answered, from listChecklists."},"value_bool":{"description":"The answer to a yes/no question.","type":"boolean"},"value_number":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The answer to a number question, in the question's unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"value_text":{"type":"string","minLength":1,"maxLength":2000,"description":"The answer to a text question."},"photos":{"default":[],"description":"Photos for this question. A photo question is answered with these alone, and no value.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."}},"required":["file_id"],"additionalProperties":false}}},"required":["checklist_item_id"],"additionalProperties":false}},"photos":{"default":[],"description":"Photos and PDFs for the check as a whole, such as the HOA approval. Upload each with uploadFile first.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."}},"required":["file_id"],"additionalProperties":false}},"outcome":{"description":"Worked out from the answers when left out. Passed is refused while anything fails.","type":"string","enum":["passed","failed"]},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"What was found, in words. Required to record a check with no checklist as passed, unless it has a photo."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["site_id","sales_order_id","method"],"additionalProperties":false,"examples":[{"site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","sales_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a80","method":"remote","answers":[{"checklist_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","value_bool":false},{"checklist_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","value_number":"44"},{"checklist_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","photos":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7f","caption":"Pad from the street side"}]}],"notes":"Pad poured Tuesday; cures next week."}]},"BookOutsidePartyInput":{"type":"object","properties":{"role":{"type":"string","enum":["crane","electrician","contractor","inspector","other"],"description":"crane, electrician, contractor, inspector or other."},"vendor_id":{"description":"The business's own supplier, when the business books and pays them (a crane company). NULL for someone the customer arranges.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"description":"Who they are, for example \"ABC Crane\" or \"Customer's electrician, Bob\". Defaults to the vendor's name. Required without vendor_id.","type":"string","minLength":1,"maxLength":200},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"How to reach them on the day, in E.164 form."},"arranged_by":{"type":"string","enum":["business","customer"],"description":"business: the business books them and is billed. customer: the customer arranges them, such as their own electrician."},"status":{"default":"needed","description":"needed: not yet asked. requested: asked, not confirmed. confirmed: they have said yes to arrive_at. done: they did their part. no_show: they did not come. cancelled: called off. Done, no_show and cancelled are final. A visit that moves sends confirmed back to requested; a visit cancelled cancels the ones not yet confirmed; a visit completed marks the confirmed ones done.","type":"string","enum":["needed","requested","confirmed"]},"arrive_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}],"description":"When they are due: a crane about half an hour after the truck, inside the delivery window; the customer's electrician when their work will be done, before the delivery. Required to confirm. ISO 8601 with an offset."},"reference":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Their booking or job reference."},{"type":"null"}],"description":"Their booking or job reference."},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the business is billed for this visit, at most 100000.00, for reporting and for passing on to the customer when a failed delivery is charged for (tenant_policy.redelivery_party_item_id). Setting it needs purchase.manage. Not posted: the bill is paid in the accounting system. Only for a party the business arranges."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"Anything worth knowing, in words."},{"type":"null"}],"description":"Anything worth knowing, in words."}},"required":["role","arranged_by"],"additionalProperties":false,"examples":[{"role":"crane","vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","arranged_by":"business","status":"confirmed","arrive_at":"2026-10-14T08:30:00-06:00","reference":"Job 5531","cost_amount":"450.00"},{"role":"electrician","name":"Customer's electrician, Bob","arranged_by":"customer","status":"requested"}]},"UpdateOutsidePartyInput":{"type":"object","properties":{"role":{"type":"string","enum":["crane","electrician","contractor","inspector","other"],"description":"crane, electrician, contractor, inspector or other."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The business's own supplier, when the business books and pays them (a crane company). NULL for someone the customer arranges."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Who they are, for example \"ABC Crane\" or \"Customer's electrician, Bob\". Defaults to the vendor's name."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"How to reach them on the day, in E.164 form."},"arranged_by":{"type":"string","enum":["business","customer"],"description":"business: the business books them and is billed. customer: the customer arranges them, such as their own electrician."},"status":{"type":"string","enum":["needed","requested","confirmed","done","no_show","cancelled"],"description":"needed: not yet asked. requested: asked, not confirmed. confirmed: they have said yes to arrive_at. done: they did their part. no_show: they did not come. cancelled: called off. Done, no_show and cancelled are final. A visit that moves sends confirmed back to requested; a visit cancelled cancels the ones not yet confirmed; a visit completed marks the confirmed ones done."},"arrive_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}],"description":"When they are due: a crane about half an hour after the truck, inside the delivery window; the customer's electrician when their work will be done, before the delivery. Required to confirm. ISO 8601 with an offset."},"reference":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Their booking or job reference."},{"type":"null"}],"description":"Their booking or job reference."},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the business is billed for this visit, at most 100000.00, for reporting and for passing on to the customer when a failed delivery is charged for (tenant_policy.redelivery_party_item_id). Setting it needs purchase.manage. Not posted: the bill is paid in the accounting system. Only for a party the business arranges."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"Anything worth knowing, in words."},{"type":"null"}],"description":"Anything worth knowing, in words."}},"additionalProperties":false,"examples":[{"status":"confirmed","arrive_at":"2026-10-13T16:00:00-06:00"},{"status":"cancelled","notes":"Called off."}]},"CompleteDeliveryInput":{"type":"object","properties":{"result":{"default":"accepted","description":"accepted: delivered as ordered. accepted_with_exceptions: delivered, with damage, a shortage or a failed check noted in exceptions. refused: the customer would not take it. not_delivered: it could not be delivered.","type":"string","enum":["accepted","accepted_with_exceptions"]},"signature_file_id":{"description":"The customer's signature: a PNG of at most 256 KiB, uploaded with uploadFile. Send signed_by_name with it.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signed_by_name":{"type":"string","minLength":1,"maxLength":200,"description":"The name of the person who signed, as they gave it. Given with the signature."},"signed_at":{"description":"When they signed, by the crew's device. Defaults to now when there is a signature.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed off, by the crew's device: latitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed off: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"staff_member_id":{"description":"Who on the crew recorded it. Defaults to the caller's staff member when they are on the crew.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"photos":{"default":[],"description":"Photos to attach first, each uploaded with uploadFile.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."},"taken_at":{"description":"When it was taken, by the device: it may be before it was uploaded, never later than now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken, by the device: latitude in degrees. Sent together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."}},"required":["file_id"],"additionalProperties":false}},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."},"customer_absent":{"default":false,"description":"True when the goods were left as agreed with nobody there to sign. Needs photos of where they were left, and delivery.override.","type":"boolean"},"exceptions":{"type":"string","minLength":1,"maxLength":5000,"description":"What was wrong with what was accepted: damage, a shortage, a check that failed. Required for accepted_with_exceptions."},"checklist_answers":{"default":[],"description":"The crew's answers to the business's delivery checklist, when it has one (getPolicy).","maxItems":100,"type":"array","items":{"type":"object","properties":{"checklist_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The question answered, from listChecklists."},"value_bool":{"description":"The answer to a yes/no question.","type":"boolean"},"value_number":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The answer to a number question, in the question's unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"value_text":{"type":"string","minLength":1,"maxLength":2000,"description":"The answer to a text question."},"photos":{"default":[],"description":"Photos for this question. A photo question is answered with these alone, and no value.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."}},"required":["file_id"],"additionalProperties":false}}},"required":["checklist_item_id"],"additionalProperties":false}},"outcome":{"type":"string","minLength":1,"maxLength":5000,"description":"What happened, written afterwards."}},"additionalProperties":false,"examples":[{"signature_file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","signed_by_name":"Maria Lopez","photos":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","caption":"Placed on the pad, filled and running"}]},{"result":"accepted_with_exceptions","signature_file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","signed_by_name":"Maria Lopez","exceptions":"Scuff on the cabinet's north corner."}]},"FailDeliveryInput":{"type":"object","properties":{"result":{"type":"string","enum":["refused","not_delivered"],"description":"accepted: delivered as ordered. accepted_with_exceptions: delivered, with damage, a shortage or a failed check noted in exceptions. refused: the customer would not take it. not_delivered: it could not be delivered."},"failure_reason":{"type":"string","enum":["site_not_ready","access_blocked","customer_absent","customer_refused","unit_damaged","wrong_unit","outside_party_no_show","weather","vehicle_or_crew","other"],"description":"Why a refused or not delivered delivery failed: site_not_ready, access_blocked, customer_absent, customer_refused, unit_damaged, wrong_unit, outside_party_no_show, weather, vehicle_or_crew or other. Required for those results, and only for them."},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"What happened, in words. Required when the delivery failed."},"arrived_at":{"description":"When the crew arrived, if not already recorded. A reason that needs the crew there needs this.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"signature_file_id":{"description":"The customer's signature on the refusal, if they gave one: a PNG uploaded with uploadFile.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signed_by_name":{"type":"string","minLength":1,"maxLength":200,"description":"The name of the person who signed, as they gave it. Given with the signature."},"signed_at":{"description":"When they signed, by the crew's device. Defaults to now when there is a signature.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed off, by the crew's device: latitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed off: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"staff_member_id":{"description":"Who on the crew recorded it. Defaults to the caller's staff member when they are on the crew.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"photos":{"default":[],"description":"Photos to attach first, each uploaded with uploadFile.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."},"taken_at":{"description":"When it was taken, by the device: it may be before it was uploaded, never later than now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken, by the device: latitude in degrees. Sent together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."}},"required":["file_id"],"additionalProperties":false}},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."},"charge_sales_order_id":{"type":"string","x-natural-key":"order_number","description":"Accepts the record's id or its order number (as a number or a string of digits)."},"waive_fee":{"default":false,"description":"True not to charge the redelivery fee the business's policy calls for, nor any outside party's bill it passes on. Needs order.discount where the delivery's open orders were sold.","type":"boolean"}},"required":["result","failure_reason","notes"],"additionalProperties":false,"examples":[{"result":"not_delivered","failure_reason":"site_not_ready","notes":"Pad still wet, forms in place. Customer agreed to rebook.","photos":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","caption":"Pad with forms still on"}]}]},"ScheduleRedeliveryInput":{"type":"object","properties":{"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Start of the window the customer was given. ISO 8601 with an offset."},"scheduled_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"End of the window the customer was given. ISO 8601 with an offset."},"crew":{"description":"Who is going. Defaults to the failed delivery's crew.","maxItems":10,"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is going."},"is_lead":{"default":false,"description":"True for the person in charge of the visit: at most one. A completed delivery is recorded as handed over by the lead.","type":"boolean"}},"required":["staff_member_id"],"additionalProperties":false}},"instructions":{"description":"Added to the failed delivery's instructions, after a line saying why it failed.","type":"string","minLength":1,"maxLength":5000}},"required":["scheduled_start","scheduled_end"],"additionalProperties":false,"examples":[{"scheduled_start":"2026-10-21T08:00:00-06:00","scheduled_end":"2026-10-21T12:00:00-06:00"}]},"UploadFileInput":{"type":"object","properties":{"encoding_format":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"],"description":"The file's type (MIME): image/jpeg, image/png or image/webp for a photo, image/png for a signature, application/pdf for a document. It must match the file's first bytes."},"content_base64":{"type":"string","minLength":4,"maxLength":7000000,"format":"base64","contentEncoding":"base64","pattern":"^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$","description":"The file's bytes, base64 encoded: at most 5 MiB before encoding. Downscale photos on the device first (2000 pixels on the long side is plenty). HEIC is not taken: send JPEG."}},"required":["encoding_format","content_base64"],"additionalProperties":false,"examples":[{"encoding_format":"image/png","content_base64":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="}]},"UpdateAttachmentInput":{"type":"object","properties":{"caption":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."},{"type":"null"}],"description":"What it shows, for example \"Pad from the street side\"."},"is_active":{"type":"boolean","description":"False once withdrawn as taken in error. Only a photo or document of a visit still scheduled, or of a job still open, can be withdrawn, never a signature or a check's photo."}},"additionalProperties":false,"examples":[{"is_active":false},{"caption":"Pad from the back gate"}]},"RecordTimeOffInput":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is out."},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day out, as a calendar date where they work."},"ends_on":{"description":"The last day out, inclusive: the same as starts_on for one day. At most a year after it. Defaults to starts_on.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"reason":{"type":"string","minLength":1,"maxLength":200,"description":"Why, in a few words, as the business wants it shown to whoever books: \"Sick\", \"Vacation\". Optional."}},"required":["staff_member_id","starts_on"],"additionalProperties":false,"examples":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","starts_on":"2026-10-14","reason":"Sick"}]},"UpdateTimeOffInput":{"type":"object","properties":{"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day out, as a calendar date where they work."},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day out, inclusive: the same as starts_on for one day. At most a year after it."},"reason":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Why, in a few words, as the business wants it shown to whoever books: \"Sick\", \"Vacation\". Optional."},{"type":"null"}],"description":"Why, in a few words, as the business wants it shown to whoever books: \"Sick\", \"Vacation\". Optional."},"is_active":{"type":"boolean","description":"False once withdrawn: they came in after all, or it was recorded by mistake."}},"additionalProperties":false,"examples":[{"ends_on":"2026-10-15"},{"is_active":false}]},"UpdateWarrantyTermsInput":{"type":"object","properties":{"filing_method":{"anyOf":[{"type":"string","enum":["portal","email","phone","fax","mail","distributor"]},{"type":"null"}],"description":"How claims are filed, for staff: portal (the manufacturer's dealer website), email, phone, fax, mail, or distributor (through the distributor the dealer buys from). NULL when not recorded."},"filing_contact":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Where to file, for staff: the portal's address, the email address, the phone number, or the distributor's name."},{"type":"null"}],"description":"Where to file, for staff: the portal's address, the email address, the phone number, or the distributor's name."},"filing_instructions":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Anything else staff need to file a claim with this manufacturer, in words: the dealer number to quote, the claim form to use."},{"type":"null"}],"description":"Anything else staff need to file a claim with this manufacturer, in words: the dealer number to quote, the claim form to use."},"filing_deadline_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}],"description":"How many days after the repair (the day its work order was completed) the manufacturer still accepts a claim. NULL sets no deadline. Claims and warranty work not sent in time are reported, not refused: whether a late claim is paid is the manufacturer's call."},"requires_proof_of_purchase":{"type":"boolean","description":"True when a claim must carry the customer's proof of purchase. Met by a unit the dealer sold (its sale is on record) or by proof_of_purchase_on_file on the claim."},"requires_serial_number":{"type":"boolean","description":"True when a claim must carry the equipment's serial number, recorded on the equipment or its unit."},"requires_photos":{"type":"boolean","description":"True when a claim must carry photos of the failure: photos_attached on the claim."},"requires_failure_description":{"type":"boolean","description":"True when a claim must say what failed and why: failure_description on the claim."},"requires_technician_notes":{"type":"boolean","description":"True when a claim must carry the technician's notes (readings, tests, error codes): technician_notes on the claim."},"requires_part_numbers":{"type":"boolean","description":"True when a claim must list the failed parts with their part numbers, when the repair replaced parts the customer was not charged for."},"failed_parts":{"type":"string","enum":["discard","hold","return"],"description":"What happens to the parts a warranty repair takes out. discard (the default): the dealer may throw them away. hold: keep each for failed_part_hold_days after the repair, in case the manufacturer asks for it. return: ship each back to the manufacturer, whose credit may depend on it. A claim for a repair that replaced parts the customer was not charged for lists them before it is sent, unless they may be discarded."},"failed_part_hold_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}],"description":"With failed_parts hold: how many days after the repair a failed part is kept before it may be discarded. Required with hold, and only with hold."},"return_authorization_required":{"type":"boolean","description":"True when a failed part may be shipped back only with the manufacturer's return authorization number."},"parts_basis":{"anyOf":[{"type":"string","enum":["none","cost","list"]},{"type":"null"}],"description":"What the manufacturer pays for the replacement parts on a claim: none (it supplies the part itself, or does not pay for parts), cost (what the part cost the dealer, plus parts_markup_percent), or list (the part's regular price in the catalog). NULL when not recorded; no expected reimbursement is worked out until both this and labor_basis are set."},"parts_markup_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"With parts_basis cost: the handling allowance the manufacturer adds to the dealer's cost, as a percentage. 0 by default. A decimal string with up to 3 decimal places, for example \"12.500\"."},"labor_basis":{"anyOf":[{"type":"string","enum":["none","actual_hours","schedule_hours"]},{"type":"null"}],"description":"How the manufacturer pays for labor: none, actual_hours (labor_rate for each hour worked: the claim's labor_hours, or else the hours of labor the customer was not charged for on the job), or schedule_hours (labor_rate for the hours the manufacturer's own labor-time schedule allows for the repair, entered as the claim's labor_hours). NULL when not recorded."},"labor_rate":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the manufacturer pays per hour of labor. Required unless labor_basis is none or not recorded. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer pays per hour of labor. Required unless labor_basis is none or not recorded."},"trip_charge":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the manufacturer pays for a trip to the customer's site, once per claim for work done at a site. NULL or 0: it pays no trip. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer pays for a trip to the customer's site, once per claim for work done at a site. NULL or 0: it pays no trip."},"shortfall_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"Where the dealer absorbs what this manufacturer pays short of an approval, when the claim is closed as paid: an expense account of the dealer's own, or Warranty Recovery itself. NULL posts it against Warranty Recovery, reducing what warranty work recovered. Accepts the record's id or its account code.","x-natural-key":"code"}},"additionalProperties":false,"examples":[{"filing_method":"portal","filing_contact":"https://dealers.example-spas.com/warranty","filing_deadline_days":30,"requires_serial_number":true,"requires_photos":true,"failed_parts":"return","return_authorization_required":true,"parts_basis":"cost","parts_markup_percent":"10","labor_basis":"schedule_hours","labor_rate":"95.00","trip_charge":"75.00"},{"failed_parts":"hold","failed_part_hold_days":60}]},"AddFailedPartInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"part_number":{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's part number. Defaults to the item's mpn."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the part is, for example \"Circulation pump\". Defaults to the item's name."},"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The failed part's own serial number, when it has one (a pump, a controller)."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the part failed. A decimal string with up to 3 decimal places, for example \"12.500\"."},"disposition":{"description":"What is to happen to it: discard, hold (kept for the manufacturer's hold period after the repair), or return (shipped back to the manufacturer). Starts as the manufacturer's claim terms say; changing it, when the manufacturer says otherwise for this part, needs warranty.manage, and is a request of its own, never with the status. Left out, the manufacturer's terms decide.","type":"string","enum":["discard","hold","return"]},"hold_days":{"description":"For a held part: how many days after the repair it is kept, from the manufacturer's terms when it was set to hold, so a later change to the terms does not shorten a hold already promised. Required with hold, and only with hold. Left out, the manufacturer's terms decide. Sending it needs warranty.manage.","type":"integer","minimum":1,"maximum":3650},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Anything else about the part, in words."}},"additionalProperties":false,"examples":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","serial_number":"P-88123"},{"description":"Circulation pump","part_number":"6000-397","quantity":"1"}]},"UpdateFailedPartInput":{"type":"object","properties":{"status":{"type":"string","enum":["on_hand","shipped","discarded"],"description":"on_hand: still at the business. shipped: sent back to the manufacturer. discarded: thrown away, once the manufacturer no longer needs it. Shipped and discarded are final. A part to return can be discarded only once its disposition says so, and a part held only once its hold is over."},"disposition":{"type":"string","enum":["discard","hold","return"],"description":"What is to happen to it: discard, hold (kept for the manufacturer's hold period after the repair), or return (shipped back to the manufacturer). Starts as the manufacturer's claim terms say; changing it, when the manufacturer says otherwise for this part, needs warranty.manage, and is a request of its own, never with the status."},"hold_days":{"type":"integer","minimum":1,"maximum":3650,"description":"For a held part: how many days after the repair it is kept, from the manufacturer's terms when it was set to hold, so a later change to the terms does not shorten a hold already promised. Required with hold, and only with hold."},"part_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's part number. Defaults to the item's mpn."},{"type":"null"}],"description":"The manufacturer's part number. Defaults to the item's mpn."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the part is, for example \"Circulation pump\". Defaults to the item's name."},"serial_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The failed part's own serial number, when it has one (a pump, a controller)."},{"type":"null"}],"description":"The failed part's own serial number, when it has one (a pump, a controller)."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the part failed. A decimal string with up to 3 decimal places, for example \"12.500\"."},"return_authorization_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's return authorization (RA or RGA) number for shipping the part back. Required to ship it when the manufacturer's terms say so."},{"type":"null"}],"description":"The manufacturer's return authorization (RA or RGA) number for shipping the part back. Required to ship it when the manufacturer's terms say so."},"shipped_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the part was shipped back. Sent with status shipped, or today; never a future date."},"carrier":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"Who carried it back, for example UPS."},{"type":"null"}],"description":"Who carried it back, for example UPS."},"tracking_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The carrier's tracking number."},{"type":"null"}],"description":"The carrier's tracking number."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Anything else about the part, in words."},{"type":"null"}],"description":"Anything else about the part, in words."}},"additionalProperties":false,"examples":[{"status":"shipped","return_authorization_number":"RGA-55102","carrier":"UPS","tracking_number":"1Z999"},{"status":"discarded"},{"disposition":"discard","notes":"Manufacturer said to scrap it."}]},"CreateWarrantyClaimInput":{"type":"object","properties":{"work_order_id":{"type":"string","x-natural-key":"work_order_number","description":"Accepts the record's id or its work order number (as a number or a string of digits)."},"equipment_warranty_id":{"description":"The warranty the claim is made under.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"vendor_id":{"description":"The manufacturer the claim is made to. Defaults to the vendor of the warranty named.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"amount_claimed":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the tenant asked for. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Correspondence and reasons, in words."},"failure_description":{"type":"string","minLength":1,"maxLength":5000,"description":"What failed and why, as the manufacturer is told: the cause, the symptom, any error code. The customer's complaint is the work order's problem, and the repair its resolution."},"technician_notes":{"type":"string","minLength":1,"maxLength":5000,"description":"The technician's notes for the manufacturer: readings taken, tests run, what was ruled out."},"proof_of_purchase_on_file":{"type":"boolean","description":"True once a copy of the customer's proof of purchase goes with the claim. Not needed for a unit the business sold: its sale is on record."},"photos_attached":{"type":"boolean","description":"True once photos of the failure go with the claim."},"labor_hours":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The hours of labor claimed: the hours worked, or the hours the manufacturer's labor-time schedule allows for the repair, as its terms say (warranty_claim_terms.labor_basis). NULL claims the hours of labor the customer was not charged for on the job. Fixed once the claim is sent. A decimal string with up to 3 decimal places, for example \"12.500\"."},"failed_parts":{"description":"The failed parts the repair took out, to track until the manufacturer has them or they may go.","maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"part_number":{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's part number. Defaults to the item's mpn."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the part is, for example \"Circulation pump\". Defaults to the item's name."},"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The failed part's own serial number, when it has one (a pump, a controller)."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the part failed. A decimal string with up to 3 decimal places, for example \"12.500\"."},"disposition":{"description":"What is to happen to it: discard, hold (kept for the manufacturer's hold period after the repair), or return (shipped back to the manufacturer). Starts as the manufacturer's claim terms say; changing it, when the manufacturer says otherwise for this part, needs warranty.manage, and is a request of its own, never with the status. Left out, the manufacturer's terms decide.","type":"string","enum":["discard","hold","return"]},"hold_days":{"description":"For a held part: how many days after the repair it is kept, from the manufacturer's terms when it was set to hold, so a later change to the terms does not shorten a hold already promised. Required with hold, and only with hold. Left out, the manufacturer's terms decide. Sending it needs warranty.manage.","type":"integer","minimum":1,"maximum":3650},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Anything else about the part, in words."}},"additionalProperties":false}}},"required":["work_order_id"],"additionalProperties":false,"examples":[{"work_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","equipment_warranty_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","amount_claimed":"412.50"},{"work_order_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","equipment_warranty_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","failure_description":"Heater element open circuit; FLO error.","photos_attached":true,"labor_hours":"1.5","failed_parts":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"}]}]},"UpdateWarrantyClaimInput":{"type":"object","properties":{"status":{"type":"string","enum":["draft","submitted","approved","denied","paid","withdrawn"],"description":"draft: being prepared. submitted: sent to the manufacturer, once it carries what the manufacturer's claim terms require. approved: they agreed to pay amount_approved, which is now owed to the tenant. denied: they refused, or withdrew an approval, which takes back what the approval posted. paid: their credit or payment has arrived (amount_paid); anything short of the approval is absorbed. withdrawn: dropped before it was sent, by the business or because its work order was cancelled; nothing was posted. A claim moves forward only: draft, submitted, then approved or denied; approved to paid or denied; a draft to withdrawn."},"claim_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's reference for the claim, once they give one."},{"type":"null"}],"description":"The manufacturer's reference for the claim, once they give one."},"amount_claimed":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the tenant asked for. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the tenant asked for."},"amount_approved":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the manufacturer agreed to pay. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer agreed to pay."},"amount_paid":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the manufacturer actually paid or credited, recorded with status paid; it defaults to amount_approved. Less than amount_approved closes the claim short: the difference is absorbed where the manufacturer's terms say (warranty_claim_terms.shortfall_account_id). A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Correspondence and reasons, in words."},{"type":"null"}],"description":"Correspondence and reasons, in words."},"failure_description":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What failed and why, as the manufacturer is told: the cause, the symptom, any error code. The customer's complaint is the work order's problem, and the repair its resolution."},{"type":"null"}],"description":"What failed and why, as the manufacturer is told: the cause, the symptom, any error code. The customer's complaint is the work order's problem, and the repair its resolution."},"technician_notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"The technician's notes for the manufacturer: readings taken, tests run, what was ruled out."},{"type":"null"}],"description":"The technician's notes for the manufacturer: readings taken, tests run, what was ruled out."},"proof_of_purchase_on_file":{"type":"boolean","description":"True once a copy of the customer's proof of purchase goes with the claim. Not needed for a unit the business sold: its sale is on record."},"photos_attached":{"type":"boolean","description":"True once photos of the failure go with the claim."},"labor_hours":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The hours of labor claimed: the hours worked, or the hours the manufacturer's labor-time schedule allows for the repair, as its terms say (warranty_claim_terms.labor_basis). NULL claims the hours of labor the customer was not charged for on the job. Fixed once the claim is sent. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The hours of labor claimed: the hours worked, or the hours the manufacturer's labor-time schedule allows for the repair, as its terms say (warranty_claim_terms.labor_basis). NULL claims the hours of labor the customer was not charged for on the job. Fixed once the claim is sent."}},"additionalProperties":false,"examples":[{"status":"submitted","claim_number":"HS-WC-77812"},{"status":"approved","amount_approved":"380.00"},{"status":"paid"},{"status":"paid","amount_paid":"340.00"},{"photos_attached":true,"technician_notes":"Element reads open; 240V at the board."}]},"CreateJobTemplateInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Z0-9-]{2,20}$","description":"The short code staff use, for example WINTERIZE: 2 to 20 uppercase letters, digits or hyphens, unique within the business."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the job is called, for example \"Spa winterize\"."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What the job involves, in words."},{"type":"null"}],"description":"What the job involves, in words."},"problem_text":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What a work order opened from it says was asked for when none is given, for example \"Winterize spa for the season\"."},{"type":"null"}],"description":"What a work order opened from it says was asked for when none is given, for example \"Winterize spa for the season\"."},"labor_pricing":{"default":"hourly","type":"string","enum":["hourly","flat"],"description":"hourly: the customer pays for the time worked, at the labor item's price. flat: the customer pays the template's charge lines whatever the time; time is recorded for cost and warranty only, and the parts it includes are used at no charge."},"labor_item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The item hourly time on these jobs is charged as. NULL uses the business's (tenant_policy.service_labor_item_id). Accepts the record's id or its SKU.","x-natural-key":"sku"},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":1,"maximum":10080,"description":"The standard time for the whole job."},{"type":"null"}],"description":"The standard time for the whole job."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service checklist the technician answers on these jobs."},"checklist_required":{"description":"True when a job from this template is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Needs checklist_id. Defaults to true when a checklist is named.","type":"boolean"},"tasks":{"default":[],"maxItems":50,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the step is."},"estimated_minutes":{"type":"integer","minimum":1,"maximum":10080,"description":"The standard time for the step."},"billing":{"description":"Who pays for time on this step by default: customer, warranty, included or no_charge. NULL: the job decides (warranty work: warranty; a flat-priced job: included; otherwise customer).","type":"string","enum":["customer","warranty","included","no_charge"]}},"required":["name"],"additionalProperties":false},"description":"Its steps, in order. On an update they replace the steps in use; jobs already open keep theirs."},"lines":{"default":[],"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"role":{"type":"string","enum":["charge","included_part","suggested_part"],"description":"charge: a service charged on every job of this kind (its flat price, a service-call fee), put on the job's charges when it is opened and handed over when a visit is completed. included_part: a part the flat price covers, up to quantity per job, used at no extra charge. suggested_part: a part the job usually needs, put on the technician's list of parts to bring and charged when used. A charge is a service; the parts are goods."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many, in the item's unit: charged, included, or to bring. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","role","quantity"],"additionalProperties":false},"description":"What it charges, includes and usually needs. On an update they replace the lines in use."}},"required":["code","name"],"additionalProperties":false,"examples":[{"code":"WINTERIZE","name":"Spa winterize","labor_pricing":"flat","problem_text":"Winterize spa for the season","checklist_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","tasks":[{"name":"Drain and blow out lines","estimated_minutes":60},{"name":"Add antifreeze"}],"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","role":"charge","quantity":"1"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","role":"included_part","quantity":"1"}]}]},"UpdateJobTemplateInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the job is called, for example \"Spa winterize\"."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What the job involves, in words."},{"type":"null"}],"description":"What the job involves, in words."},"problem_text":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What a work order opened from it says was asked for when none is given, for example \"Winterize spa for the season\"."},{"type":"null"}],"description":"What a work order opened from it says was asked for when none is given, for example \"Winterize spa for the season\"."},"labor_pricing":{"type":"string","enum":["hourly","flat"],"description":"hourly: the customer pays for the time worked, at the labor item's price. flat: the customer pays the template's charge lines whatever the time; time is recorded for cost and warranty only, and the parts it includes are used at no charge."},"labor_item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The item hourly time on these jobs is charged as. NULL uses the business's (tenant_policy.service_labor_item_id). Accepts the record's id or its SKU.","x-natural-key":"sku"},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":1,"maximum":10080,"description":"The standard time for the whole job."},{"type":"null"}],"description":"The standard time for the whole job."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service checklist the technician answers on these jobs."},"checklist_required":{"type":"boolean","description":"True when a job from this template is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Needs checklist_id."},"is_active":{"type":"boolean","description":"False once the business no longer does this kind of job. Jobs already opened from it keep what was copied."},"tasks":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the step is."},"estimated_minutes":{"type":"integer","minimum":1,"maximum":10080,"description":"The standard time for the step."},"billing":{"description":"Who pays for time on this step by default: customer, warranty, included or no_charge. NULL: the job decides (warranty work: warranty; a flat-priced job: included; otherwise customer).","type":"string","enum":["customer","warranty","included","no_charge"]}},"required":["name"],"additionalProperties":false},"description":"Its steps, in order. On an update they replace the steps in use; jobs already open keep theirs."},"lines":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"role":{"type":"string","enum":["charge","included_part","suggested_part"],"description":"charge: a service charged on every job of this kind (its flat price, a service-call fee), put on the job's charges when it is opened and handed over when a visit is completed. included_part: a part the flat price covers, up to quantity per job, used at no extra charge. suggested_part: a part the job usually needs, put on the technician's list of parts to bring and charged when used. A charge is a service; the parts are goods."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many, in the item's unit: charged, included, or to bring. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","role","quantity"],"additionalProperties":false},"description":"What it charges, includes and usually needs. On an update they replace the lines in use."}},"additionalProperties":false,"examples":[{"estimated_minutes":90},{"is_active":false},{"tasks":[{"name":"Diagnose"},{"name":"Replace heater"}]}]},"AddWorkOrderTaskInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the step is."},"position":{"description":"Where it comes in the job, smallest first. Defaults to after the last.","type":"integer","minimum":1,"maximum":1000},"estimated_minutes":{"type":"integer","minimum":1,"maximum":10080,"description":"The standard time for the step."},"billing":{"description":"Who pays, by default, for time and parts on this step. customer: charged at the business's prices. warranty: claimed from the manufacturer, at 0 to the customer; only on warranty work. included: covered by the job's flat price; only on a flat-priced job. no_charge: given away, such as a callback or goodwill. no_charge needs order.discount at the job's location.","type":"string","enum":["customer","warranty","included","no_charge"]},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"What the technician noted on the step."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Replace pump seal","estimated_minutes":45},{"name":"Callback: recheck heater","billing":"no_charge"}]},"UpdateWorkOrderTaskInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the step is."},"position":{"type":"integer","minimum":1,"maximum":1000,"description":"Where it comes in the job, smallest first."},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":1,"maximum":10080,"description":"The standard time for the step."},{"type":"null"}],"description":"The standard time for the step."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays, by default, for time and parts on this step. customer: charged at the business's prices. warranty: claimed from the manufacturer, at 0 to the customer; only on warranty work. included: covered by the job's flat price; only on a flat-priced job. no_charge: given away, such as a callback or goodwill. no_charge needs order.discount at the job's location."},"status":{"type":"string","enum":["open","done","skipped"],"description":"open: still to do. done: finished (done_at). skipped: not needed after all."},"done_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"When it was finished, by the technician's device. Defaults to now when it is marked done."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"What the technician noted on the step."},{"type":"null"}],"description":"What the technician noted on the step."}},"additionalProperties":false,"examples":[{"status":"done","done_at":"2026-10-03T10:40:00-06:00"},{"billing":"warranty"}]},"RecordLaborTimeInput":{"type":"object","properties":{"staff_member_id":{"description":"Whose time. Defaults to the caller's staff member; another person's needs labor.manage at the job's location.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"appointment_id":{"description":"The visit the time was spent on, if any: completing it charges its time.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"work_order_task_id":{"description":"The step of the job the time was spent on, if any.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"activity":{"default":"work","description":"work: on the job. travel: getting to it. Fixed.","type":"string","enum":["work","travel"]},"billing":{"description":"Who pays for the time. customer: charged by the hour at the labor item's price. warranty: charged at 0 and claimed from the manufacturer; only on warranty work with a warranty in force on the day, never for travel. included: covered by the job's flat price (or, with no job, a care plan). no_charge: given away. Defaults to the step's billing, travel to tenant_policy.travel_time_billing. no_charge needs order.discount at the job's location.","type":"string","enum":["customer","warranty","included","no_charge"]},"started_at":{"description":"When the timer was started, by the technician's device. NULL for time entered as a duration. Send it alone to start a timer, or with ended_at for time already done.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"ended_at":{"description":"When the timer was stopped, by the device. NULL while it is running, and for time entered as a duration. At most 24 hours after started_at.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"worked_on":{"description":"The business date of the work, at the job's location: worked out from started_at, and sent with a duration (today by default).","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"minutes":{"description":"How long, from 1 to 1440: worked out from start and stop to the minute, or sent as a duration. NULL while the timer runs. Send it, with no times, for a duration.","type":"integer","minimum":1,"maximum":1440},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"What was done in the time."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"started_at":"2026-10-03T09:05:00-06:00","work_order_task_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-labor-1"},{"minutes":30,"activity":"travel","worked_on":"2026-10-03","idempotency_key":"0199a3c4-labor-2"}]},"UpdateLaborEntryInput":{"type":"object","properties":{"stop":{"type":"boolean","const":true,"description":"Stop a running timer now. Send ended_at instead for the device's time."},"ended_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"When the timer was stopped, by the device. NULL while it is running, and for time entered as a duration. At most 24 hours after started_at."},"started_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"When the timer was started, by the technician's device. NULL for time entered as a duration."},"minutes":{"type":"integer","minimum":1,"maximum":1440,"description":"How long, from 1 to 1440: worked out from start and stop to the minute, or sent as a duration. NULL while the timer runs. Only for time entered as a duration."},"worked_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the work, at the job's location: worked out from started_at, and sent with a duration (today by default). Only for time entered as a duration."},"work_order_task_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The step of the job the time was spent on, if any."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays for the time. customer: charged by the hour at the labor item's price. warranty: charged at 0 and claimed from the manufacturer; only on warranty work with a warranty in force on the day, never for travel. included: covered by the job's flat price (or, with no job, a care plan). no_charge: given away. Defaults to the step's billing, travel to tenant_policy.travel_time_billing. no_charge needs order.discount at the job's location."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"What was done in the time."},{"type":"null"}],"description":"What was done in the time."},"void_reason":{"type":"string","minLength":1,"maxLength":500,"description":"Void the entry as recorded in error, saying why. Time already charged cannot be voided."}},"additionalProperties":false,"examples":[{"stop":true},{"ended_at":"2026-10-03T10:40:00-06:00"},{"void_reason":"Started on the wrong job."}]},"RecordPartsUsedInput":{"type":"object","properties":{"staff_member_id":{"description":"Who used them. Defaults to the caller's staff member; their van is where they come from.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"appointment_id":{"description":"The visit it was used on, if any: one of the job's.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"parts":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many were used, in the item's unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"from_location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"billing":{"description":"Who pays for it. customer: at unit_price. warranty: at 0, claimed from the manufacturer; only on warranty work with a warranty in force on the day. included: at 0, covered by the job's flat price up to what it includes. no_charge: at 0, given away. A warranty job takes neither included nor no-charge parts: every part at 0 on it is claimed. Defaults to the step's billing, else warranty on warranty work, included on a flat-priced job while the price includes more of it, else customer. no_charge needs order.discount at the job's location.","type":"string","enum":["customer","warranty","included","no_charge"]},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For a customer part: the price, when not the item's. Below it needs the discount allowance. A decimal string with up to 2 decimal places, for example \"12.50\"."},"work_order_task_id":{"description":"The step of the job it was used on, if any: its billing is the part's default.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"serialized_unit_id":{"description":"For a serial-tracked part: the unit installed.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"stock_lot_id":{"description":"For a lot-tracked part: the lot it came from. Left out, the first lots to expire are taken.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"failed_part_number":{"type":"string","minLength":1,"maxLength":100,"description":"For a warranty part: the part number of the part taken out, for the claim."},"failed_serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"For a warranty part: the serial number of the part taken out."},"failed_part_description":{"type":"string","minLength":1,"maxLength":2000,"description":"For a warranty part: what failed, in words."},"used_at":{"description":"When it was used, by the technician's device. Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"Anything worth recording about it."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key for this part, sent again unchanged on a retry: a repeat does not use the part again. One per part."}},"required":["item_id","quantity","idempotency_key"],"additionalProperties":false},"description":"The parts used. All are recorded, or none."}},"required":["parts"],"additionalProperties":false,"examples":[{"parts":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"1","billing":"warranty","failed_part_number":"58353","idempotency_key":"0199a3c4-part-1"}]}]},"TakeBackPartInput":{"type":"object","properties":{"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many to take back. Defaults to all that was used. A decimal string with up to 3 decimal places, for example \"12.500\"."},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why it is taken back: recorded in error, not used after all."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["reason","idempotency_key"],"additionalProperties":false,"examples":[{"reason":"Recorded on the wrong job.","idempotency_key":"0199a3c4-back-1"}]},"AddWorkOrderPhotosInput":{"type":"object","properties":{"photos":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The photo, from uploadFile."},"shows":{"description":"For a job's photo: what it shows. fault: the failure as found. repair: the work as left. serial_plate: the equipment's serial plate. site: the site. other. Manufacturers ask for the fault and the serial plate with a warranty claim.","type":"string","enum":["fault","repair","serial_plate","site","other"]},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."},"taken_at":{"description":"When it was taken, by the device: it may be before it was uploaded, never later than now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken, by the device: latitude in degrees. Sent together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was taken: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."}},"required":["file_id"],"additionalProperties":false},"description":"The photos. A file the job already has is not attached twice."}},"required":["photos"],"additionalProperties":false,"examples":[{"photos":[{"file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","shows":"fault","caption":"Heater element burnt at the terminal"}]}]},"RecordServiceChecklistInput":{"type":"object","properties":{"checklist_id":{"description":"The service checklist answered. Defaults to the job's.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"appointment_id":{"description":"The visit it was answered on, if any.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"answers":{"default":[],"description":"The answers, one per question, each with any photos.","maxItems":100,"type":"array","items":{"type":"object","properties":{"checklist_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The question answered, from listChecklists."},"value_bool":{"description":"The answer to a yes/no question.","type":"boolean"},"value_number":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The answer to a number question, in the question's unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"value_text":{"type":"string","minLength":1,"maxLength":2000,"description":"The answer to a text question."},"photos":{"default":[],"description":"Photos for this question. A photo question is answered with these alone, and no value.","maxItems":20,"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file, from uploadFile."},"caption":{"type":"string","minLength":1,"maxLength":500,"description":"What it shows, for example \"Pad from the street side\"."}},"required":["file_id"],"additionalProperties":false}}},"required":["checklist_item_id"],"additionalProperties":false}},"outcome":{"description":"Worked out from the answers when left out. Passed is refused if any answer fails.","type":"string","enum":["passed","failed"]},"notes":{"type":"string","minLength":1,"maxLength":2000,"description":"What was found, in words. Required to record a check with no checklist as passed, unless it has a photo."},"staff_member_id":{"description":"Who answered it. Defaults to the caller's staff member.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"answers":[{"checklist_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","value_number":"19.5"},{"checklist_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","value_bool":true}],"idempotency_key":"0199a3c4-check-1"}]},"SignWorkOrderInput":{"type":"object","properties":{"kind":{"type":"string","enum":["authorization","completion"],"description":"authorization: the customer agreed to the work before it was done. completion: they accepted it as done, and its charges."},"appointment_id":{"description":"The visit it was signed on, if any: a completion on a visit is what finishing it needs where the business requires a signature.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signature_file_id":{"description":"The customer's signature, a PNG uploaded with uploadFile. Send with signed_by_name.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signed_by_name":{"type":"string","minLength":1,"maxLength":200,"description":"The name of the person who signed, as they gave it. Given with the signature."},"signed_at":{"description":"When they signed, by the technician's device, or when it was recorded with nobody there. Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"customer_absent":{"default":false,"description":"True for a completion recorded with nobody there to sign. Needs a photo on the job, and labor.manage where the business requires a signature.","type":"boolean"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed, by the device: latitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"amount_authorized":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For an authorization: the most the customer agreed to, before tax. NULL authorizes the charges shown. A decimal string with up to 2 decimal places, for example \"12.50\"."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["kind","idempotency_key"],"additionalProperties":false,"examples":[{"kind":"authorization","signature_file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","signed_by_name":"Pat Lee","amount_authorized":"650.00","idempotency_key":"0199a3c4-sign-1"}]},"CompleteServiceVisitInput":{"type":"object","properties":{"outcome":{"type":"string","minLength":1,"maxLength":5000,"description":"What happened, written afterwards."},"finished_at":{"description":"When the work ended, by the device: stops the crew's timers still running on this visit there. Now when left out.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"diagnosis":{"type":"string","minLength":1,"maxLength":5000,"description":"What the technician found: the cause, readings, error codes. The customer's words stay in problem, what was done goes in resolution. A warranty claim's technician notes start from it."},"resolution":{"type":"string","minLength":1,"maxLength":5000,"description":"What was found and what was done. Required to complete the work order."},"complete_work_order":{"default":false,"description":"True when the job is done with this visit: completes the work order too. Needs resolution, unless the job has one.","type":"boolean"},"sign_off":{"description":"The customer's completion sign-off, given at the end of the visit.","type":"object","properties":{"appointment_id":{"description":"The visit it was signed on, if any: a completion on a visit is what finishing it needs where the business requires a signature.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signature_file_id":{"description":"The customer's signature, a PNG uploaded with uploadFile. Send with signed_by_name.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signed_by_name":{"type":"string","minLength":1,"maxLength":200,"description":"The name of the person who signed, as they gave it. Given with the signature."},"signed_at":{"description":"When they signed, by the technician's device, or when it was recorded with nobody there. Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"customer_absent":{"default":false,"description":"True for a completion recorded with nobody there to sign. Needs a photo on the job, and labor.manage where the business requires a signature.","type":"boolean"},"latitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed, by the device: latitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."},"longitude":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Where it was signed: longitude in degrees. A decimal string with up to 6 decimal places, for example \"12.500000\"."}},"additionalProperties":false}},"required":["outcome"],"additionalProperties":false,"examples":[{"outcome":"Replaced heater element; tested 30 minutes.","finished_at":"2026-10-03T11:20:00-06:00","resolution":"Replaced heater element. Amps 19.5.","complete_work_order":true,"sign_off":{"signature_file_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","signed_by_name":"Pat Lee"}}]},"PriceServiceVisitInput":{"type":"object","properties":{"finished_at":{"description":"When the work would end, by the device: the timers still running are priced as stopped there. Now when left out. Send the same finished_at to completeServiceVisit and it charges exactly this.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"additionalProperties":false,"examples":[{"finished_at":"2026-10-03T11:20:00-06:00"}]},"SetLaborCostRateInput":{"type":"object","properties":{"effective_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day the rate applies to. It applies until the next rate's day. Fixed."},"cost_rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one hour costs the business. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"type":"string","minLength":1,"maxLength":500,"description":"How it was worked out, in words."}},"required":["effective_on","cost_rate"],"additionalProperties":false,"examples":[{"effective_on":"2026-01-01","cost_rate":"38.50","notes":"Wage 28.00 plus 37.5% burden"}]},"CreateEquipmentInput":{"type":"object","properties":{"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is installed: one of the owner's own sites. NULL until known, for example a unit the customer carried away."},"item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The catalog item this is a unit of, when the tenant carries it. Accepts the record's id or its SKU.","x-natural-key":"sku"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What to call it, for example \"Grandee hot tub\"."},"brand":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The brand, for equipment not in the tenant's catalog."},{"type":"null"}],"description":"The brand, for equipment not in the tenant's catalog."},"model":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The model, for equipment not in the tenant's catalog."},{"type":"null"}],"description":"The model, for equipment not in the tenant's catalog."},"serial_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The serial number, for equipment not in the tenant's stock records."},{"type":"null"}],"description":"The serial number, for equipment not in the tenant's stock records."},"installed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date it was delivered or installed."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff, for example where the breaker is."},{"type":"null"}],"description":"Free-form notes for staff, for example where the breaker is."}},"required":["customer_id","name"],"additionalProperties":false,"examples":[{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","name":"Cal Spas hot tub","brand":"Cal Spas","model":"Platinum 838","serial_number":"CS-19-88213","installed_on":"2019-06-01","notes":"Bought elsewhere. Breaker is in the garage."}]},"UpdateEquipmentInput":{"type":"object","properties":{"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is installed: one of the owner's own sites. NULL until known, for example a unit the customer carried away."},"item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The catalog item this is a unit of, when the tenant carries it. Accepts the record's id or its SKU.","x-natural-key":"sku"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What to call it, for example \"Grandee hot tub\"."},"brand":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The brand, for equipment not in the tenant's catalog."},{"type":"null"}],"description":"The brand, for equipment not in the tenant's catalog."},"model":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"The model, for equipment not in the tenant's catalog."},{"type":"null"}],"description":"The model, for equipment not in the tenant's catalog."},"serial_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The serial number, for equipment not in the tenant's stock records."},{"type":"null"}],"description":"The serial number, for equipment not in the tenant's stock records."},"installed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date it was delivered or installed."},"removed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date it was returned, removed or replaced."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff, for example where the breaker is."},{"type":"null"}],"description":"Free-form notes for staff, for example where the breaker is."},"is_active":{"type":"boolean","description":"False once the customer no longer has it. Its service history stays."}},"additionalProperties":false,"examples":[{"site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"removed_on":"2026-09-30","is_active":false}]},"AddEquipmentWarrantyInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What is covered, for example \"Shell structure\" or \"Parts and labor\"."},"provider":{"type":"string","enum":["manufacturer","dealer"],"description":"Who stands behind it: manufacturer or dealer (the tenant itself)."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manufacturer: required when provider is manufacturer, and NULL when the dealer stands behind it. Claims go to this vendor."},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First day of cover, usually the delivery date."},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day of cover."},"terms":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What the warranty covers and excludes, in words."},{"type":"null"}],"description":"What the warranty covers and excludes, in words."}},"required":["name","provider","starts_on","ends_on"],"additionalProperties":false,"examples":[{"name":"Shell structure","provider":"manufacturer","vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","starts_on":"2026-07-15","ends_on":"2036-07-14"},{"name":"Dealer labor","provider":"dealer","starts_on":"2026-07-15","ends_on":"2027-07-14"}]},"UpdateEquipmentWarrantyInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What is covered, for example \"Shell structure\" or \"Parts and labor\"."},"provider":{"type":"string","enum":["manufacturer","dealer"],"description":"Who stands behind it: manufacturer or dealer (the tenant itself)."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manufacturer: required when provider is manufacturer, and NULL when the dealer stands behind it. Claims go to this vendor."},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First day of cover, usually the delivery date."},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day of cover."},"terms":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What the warranty covers and excludes, in words."},{"type":"null"}],"description":"What the warranty covers and excludes, in words."},"is_active":{"type":"boolean","description":"False once the warranty is void, for example after the equipment changed hands."}},"additionalProperties":false,"examples":[{"is_active":false}]},"OpenWorkOrderInput":{"type":"object","properties":{"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"site_id":{"description":"Where the work is done: one of the customer's own sites. NULL for work done at the location, for example on a unit brought in.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"equipment_id":{"description":"The equipment being worked on, which must be the customer's own. On warranty work its warranties decide what is free, so changing it then needs warranty.manage, and is refused once parts or time are recorded under it.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"is_warranty":{"default":false,"description":"True when the work is covered by a warranty, so the customer is not charged for the covered part. Setting or clearing it needs warranty.manage at the job's location, and it is fixed once parts not billed to the customer, or time billed to warranty, are recorded on the job.","type":"boolean"},"job_template_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its job template code."},"problem":{"description":"What the customer reported or asked for, in their terms. Required unless the job is opened from a template.","type":"string","minLength":1,"maxLength":5000},"labor_item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"estimated_minutes":{"description":"The standard time for the whole job, from its template or as estimated. Compared with the time recorded.","type":"integer","minimum":1,"maximum":10080},"checklist_id":{"description":"The service checklist the technician answers on the job, such as amperage readings and a leak check.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"checklist_required":{"description":"True when the job is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Dropping it, or answering another checklist instead, needs labor.manage.","type":"boolean"}},"required":["customer_id","location_id"],"additionalProperties":false,"examples":[{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","equipment_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","problem":"Heater trips the breaker after about an hour."},{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","job_template_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7f"}]},"UpdateWorkOrderInput":{"type":"object","properties":{"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the work is done: one of the customer's own sites. NULL for work done at the location, for example on a unit brought in."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment being worked on, which must be the customer's own. On warranty work its warranties decide what is free, so changing it then needs warranty.manage, and is refused once parts or time are recorded under it."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"is_warranty":{"type":"boolean","description":"True when the work is covered by a warranty, so the customer is not charged for the covered part. Setting or clearing it needs warranty.manage at the job's location, and it is fixed once parts not billed to the customer, or time billed to warranty, are recorded on the job."},"problem":{"type":"string","minLength":1,"maxLength":5000,"description":"What the customer reported or asked for, in their terms."},"diagnosis":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What the technician found: the cause, readings, error codes. The customer's words stay in problem, what was done goes in resolution. A warranty claim's technician notes start from it."},{"type":"null"}],"description":"What the technician found: the cause, readings, error codes. The customer's words stay in problem, what was done goes in resolution. A warranty claim's technician notes start from it."},"resolution":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"What was found and what was done. Required to complete the work order."},{"type":"null"}],"description":"What was found and what was done. Required to complete the work order."},"labor_item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The item hourly customer and warranty time on this job is charged as: a service sold by the hour, in fractions. NULL uses the business's (tenant_policy.service_labor_item_id). One priced below the template's or the business's needs order.discount at the job's location. Accepts the record's id or its SKU.","x-natural-key":"sku"},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":1,"maximum":10080},{"type":"null"}],"description":"The standard time for the whole job, from its template or as estimated. Compared with the time recorded."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service checklist the technician answers on the job, such as amperage readings and a leak check."},"checklist_required":{"type":"boolean","description":"True when the job is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Dropping it, or answering another checklist instead, needs labor.manage."}},"additionalProperties":false,"examples":[{"is_warranty":true},{"resolution":"Replaced the heater element. Tested 2 hours."}]},"AddWorkOrderChargesInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many the customer is buying, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists. May be left out when the item is sold in only one pack.","type":"string","minLength":1,"maxLength":40},"pack_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. Below the pack's price needs order.discount."},"unit_price":{"description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. Below the item's price needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"discount_amount":{"description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. Needs order.discount.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says on the order. Defaults to the item's name."},"serialized_unit_id":{"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over. Left out with a unit named, the line takes the unit's options. Each option's price difference is added to the item's price unless unit_price is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false},"description":"Parts and labor, as sales order lines. Under warranty, price the covered part at 0 (needs order.discount)."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"1"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","quantity":"1.5"}]}]},"CompleteWorkOrderInput":{"type":"object","properties":{"resolution":{"description":"What was found and what was done. Required to complete the work order. Send it unless it is already recorded.","type":"string","minLength":1,"maxLength":5000}},"additionalProperties":false,"examples":[{"resolution":"Replaced the heater element. Tested 2 hours."}]},"PutRouteInOrderInput":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Whose day to put in order."},"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day, at the stores the visits are booked from."},"stops":{"minItems":1,"maxItems":60,"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A visit on their day that is still scheduled."},"scheduled_start":{"description":"Start of the window the customer was given. The new window the customer is given, ISO 8601 with an offset.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduled_end":{"description":"End of the window the customer was given. ISO 8601 with an offset.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["appointment_id"],"additionalProperties":false},"description":"Every visit on their day still scheduled, first stop first, each once. A window sent narrows or moves the one the customer was given, on the same day."}},"required":["staff_member_id","date","stops"],"additionalProperties":false,"examples":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","date":"2026-10-14","stops":[{"appointment_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","scheduled_start":"2026-10-14T08:00:00-06:00","scheduled_end":"2026-10-14T10:00:00-06:00"},{"appointment_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d"}]}]},"MoveRouteStopInput":{"type":"object","properties":{"to_staff_member_id":{"description":"Who takes the visit: they go instead of from_staff_member_id, as lead if that person was. Leave it out to keep who goes.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"from_staff_member_id":{"description":"Who no longer goes, when the visit has more than one person on it. Leave it out when one person or nobody is on it.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"date":{"description":"The day it moves to, at its store: the window keeps its times of day unless a new one is sent.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"position":{"description":"Where it comes on the day of whoever takes it, 1 first. Leave it out to put it last when it changes day or person, or to leave it where it is.","type":"integer","minimum":1,"maximum":60},"scheduled_start":{"description":"Start of the window the customer was given. The new window the customer is given, ISO 8601 with an offset.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduled_end":{"description":"End of the window the customer was given. ISO 8601 with an offset.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"additionalProperties":false,"examples":[{"to_staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","position":2},{"date":"2026-10-15","position":1}]},"CreateFloorPlanLenderInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's name, unique within the business."},"interest_rate_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The yearly interest rate, as a percent, charged on what is owed on each unit after its free days. Used to estimate interest accrued; interest is booked as the lender bills it. A decimal string with up to 3 decimal places, for example \"12.500\"."},"free_days":{"default":0,"type":"integer","minimum":0,"maximum":3650,"description":"Days after a unit is financed before interest starts (free flooring). 0 for none."},"curtailments":{"default":[],"maxItems":24,"type":"array","items":{"type":"object","properties":{"after_days":{"type":"integer","minimum":1,"maximum":3650,"description":"Days after a unit is financed when this part falls due."},"percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The percent of the amount financed due then. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["after_days","percent"],"additionalProperties":false,"description":"One step of a lender's curtailment schedule."},"description":"The curtailment schedule, earliest first: each age (after_days, each later than the one before) and the percent of the amount financed due then, together at most 100. Empty for none. The curtailment schedule's ages: each is a number of days after a unit is financed when a part of the amount financed falls due, in increasing order. Empty when the lender asks for no curtailments."},"payoff_due_days":{"default":0,"type":"integer","minimum":0,"maximum":365,"description":"Days after a unit is sold by which what is left on it must be paid off. 0 for the day it is sold."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff: account numbers, contacts, audit dates."},{"type":"null"}],"description":"Free-form notes for staff: account numbers, contacts, audit dates."}},"required":["name","interest_rate_percent"],"additionalProperties":false,"examples":[{"name":"Northpoint Commercial Finance","interest_rate_percent":"8.500","free_days":90,"curtailments":[{"after_days":180,"percent":"10.00"},{"after_days":270,"percent":"10.00"}],"payoff_due_days":10}]},"UpdateFloorPlanLenderInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The lender's name, unique within the business."},"interest_rate_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The yearly interest rate, as a percent, charged on what is owed on each unit after its free days. Used to estimate interest accrued; interest is booked as the lender bills it. A decimal string with up to 3 decimal places, for example \"12.500\"."},"free_days":{"type":"integer","minimum":0,"maximum":3650,"description":"Days after a unit is financed before interest starts (free flooring). 0 for none."},"curtailments":{"maxItems":24,"type":"array","items":{"type":"object","properties":{"after_days":{"type":"integer","minimum":1,"maximum":3650,"description":"Days after a unit is financed when this part falls due."},"percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The percent of the amount financed due then. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["after_days","percent"],"additionalProperties":false,"description":"One step of a lender's curtailment schedule."},"description":"The curtailment schedule, earliest first: each age (after_days, each later than the one before) and the percent of the amount financed due then, together at most 100. Empty for none. The curtailment schedule's ages: each is a number of days after a unit is financed when a part of the amount financed falls due, in increasing order. Empty when the lender asks for no curtailments."},"payoff_due_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days after a unit is sold by which what is left on it must be paid off. 0 for the day it is sold."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff: account numbers, contacts, audit dates."},{"type":"null"}],"description":"Free-form notes for staff: account numbers, contacts, audit dates."},"is_active":{"type":"boolean","description":"False once the business no longer floors units with this lender. Retired only when none of its units still owe it anything."}},"additionalProperties":false,"examples":[{"interest_rate_percent":"9.250"},{"is_active":false}]},"CreateFloorPlanLoanInput":{"type":"object","properties":{"serialized_unit_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The unit financed. A unit has at most one loan that is not paid off."},"floor_plan_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender. Its terms set the curtailments, the payoff date and the interest."},"amount_financed":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the lender paid for the unit and the business owes for it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"financed_on":{"description":"The date the lender paid, from which curtailments, interest and the age on the floor plan count. Defaults to today; never in the future.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["serialized_unit_id","floor_plan_lender_id","amount_financed","idempotency_key"],"additionalProperties":false,"examples":[{"serialized_unit_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","floor_plan_lender_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount_financed":"8400.00","idempotency_key":"0199a3c4-floor-0001"}]},"PayFloorPlanLoanInput":{"type":"object","properties":{"principal_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the payment takes off what is owed on the unit: a curtailment, or the payoff when it clears the rest. 0 for interest only. A decimal string with up to 2 decimal places, for example \"12.50\"."},"pay_off":{"description":"True to pay off everything still owed on the unit, instead of sending principal_amount.","type":"boolean"},"interest_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Interest paid, posted to Floor Plan Interest. 0 for principal only. A decimal string with up to 2 decimal places, for example \"12.50\"."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"paid_on":{"description":"The date it was paid, the date of its journal entry. Defaults to today; never in the future, nor before the loan was financed.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording: the lender's statement or reference."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["ledger_account_id","idempotency_key"],"additionalProperties":false,"examples":[{"principal_amount":"840.00","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-curtail-0001"},{"pay_off":true,"ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-payoff-0001"}]},"PayFloorPlanInterestInput":{"type":"object","properties":{"interest_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Interest paid, posted to Floor Plan Interest. 0 for principal only. A decimal string with up to 2 decimal places, for example \"12.50\"."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"paid_on":{"description":"The date it was paid, the date of its journal entry. Defaults to today; never in the future, nor before the loan was financed.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording: the lender's statement or reference."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["interest_amount","ledger_account_id","idempotency_key"],"additionalProperties":false,"examples":[{"interest_amount":"412.37","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","note":"September statement","idempotency_key":"0199a3c4-interest-0001"}]},"CreateEventConsumerInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"What reads the feed, unique within the tenant: \"quickbooks\", \"automations\"."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"quickbooks"}]},"AcknowledgeEventsInput":{"type":"object","properties":{"event_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The id of the last event handled: the last one getNewEvents returned."}},"required":["event_id"],"additionalProperties":false,"examples":[{"event_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"SetExternalLinkInput":{"type":"object","properties":{"system":{"type":"string","pattern":"^[a-z][a-z0-9_]{1,39}$","description":"The outside system, as a short lowercase key: quickbooks, stripe, woocommerce."},"table_name":{"type":"string","pattern":"^[a-z][a-z0-9_]*$","description":"The table of the row here."},"row_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The id of the row here."},"external_id":{"type":"string","minLength":1,"maxLength":200,"description":"The record's id in the outside system."},"is_active":{"default":true,"description":"False once the outside record is gone or the link was wrong.","type":"boolean"}},"required":["system","table_name","row_id","external_id"],"additionalProperties":false,"examples":[{"system":"quickbooks","table_name":"customer","row_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","external_id":"148"}]},"CreateJournalExportInput":{"type":"object","properties":{"system":{"type":"string","pattern":"^[a-z][a-z0-9_]{1,39}$","description":"The accounting system the batch is for: quickbooks."},"through_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The batch holds every entry dated on or before this that was not already in a batch."}},"required":["system","through_date"],"additionalProperties":false,"examples":[{"system":"quickbooks","through_date":"2026-09-30"}]},"UpdateJournalExportInput":{"type":"object","properties":{"status":{"type":"string","enum":["exported","failed"],"description":"pending: made, not yet confirmed received. exported: the accounting system has it. failed: it was refused; status_reason says why, and the adapter tries the same batch again."},"status_reason":{"type":"string","minLength":1,"maxLength":2000,"description":"Why the batch failed."}},"required":["status"],"additionalProperties":false,"examples":[{"status":"exported"},{"status":"failed","status_reason":"QuickBooks refused account 6100: not mapped."}]},"AdjustStockInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"kind":{"type":"string","enum":["opening","adjustment"],"description":"opening: stock already on the shelf when the business started using the system. adjustment: a correction for damage, loss or a find, with its reason in adjustment_reason_id. Counts, receipts and transfers have operations of their own."},"adjustment_reason_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its reason code."},"note":{"description":"What happened, in words. Required when the reason asks for one.","type":"string","minLength":1,"maxLength":1000},"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much stock changes: positive into stock, negative out of it. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"Cost per unit. For stock coming in, send either this or extended_cost; with neither, the current average cost is used. For stock going out it is calculated and any value sent is ignored. Rounded to four places: extended_cost is the exact figure. For a revaluation: the unit's new cost, which is sent. For a landed cost: what it added per unit of the stock it went to. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"extended_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Total cost of the movement, always positive. For stock coming in, send it instead of unit_cost when the total is what is known exactly: a vendor invoice line, a transfer, a return. For stock going out it is calculated by the tenant's costing method and is the cost of the goods. For a serialized unit it is the unit's own cost. For a revaluation it is the amount written off: the unit's old cost less its new one. For a landed cost it is the amount added to stock value. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"serialized_unit_id":{"description":"The individual unit that moved. Required for a serial-tracked item, not allowed otherwise.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot coming in, or the lot to take stock out of. Left out, stock coming in goes into the unknown lot where the business allows it, and stock going out leaves the first lots to expire. A code not yet recorded for the item is recorded as a new lot. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"expires_on":{"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire. Recorded on a new lot, or on a lot that has none yet.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["item_id","quantity"],"additionalProperties":false},"description":"One line per item, or per unit of a serial-tracked item."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["location_id","kind","lines","idempotency_key"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","kind":"opening","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"24","unit_cost":"18.5000"}],"idempotency_key":"0199a3c4-opening-boi-chemicals"},{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","kind":"adjustment","adjustment_reason_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","note":"Two buckets crushed by the forklift","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"-2"}],"idempotency_key":"0199a3c4-forklift-2026-10-02"}]},"CreateSerializedUnitInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's serial number, uppercase, unique within the item."},"cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What this unit cost the tenant. The unit enters and leaves stock at exactly this cost. It can be corrected until the unit first moves into stock. From then on only a write-down changes it, lowering it while the unit is in stock. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff, for example freight damage or a floor-model history."},"options":{"description":"For a model built with options: how this unit is built. Set from the purchase order line it arrived on; recorded by hand for a unit that came another way. NULL when not recorded. An order line naming the unit takes it.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id","serial_number","cost"],"additionalProperties":false,"examples":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","serial_number":"HS26-004471","cost":"10450.00","notes":"Floor model, Boise"}]},"UpdateSerializedUnitInput":{"type":"object","properties":{"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The manufacturer's serial number, uppercase, unique within the item."},"cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What this unit cost the tenant. The unit enters and leaves stock at exactly this cost. It can be corrected until the unit first moves into stock. From then on only a write-down changes it, lowering it while the unit is in stock. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff, for example freight damage or a floor-model history."},{"type":"null"}],"description":"Free-form notes for staff, for example freight damage or a floor-model history."},"options":{"anyOf":[{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false},"description":"For a model built with options (getItem, option_groups): one option from each group chosen, every required group answered. Each option's price difference is added to the price."},{"type":"null"}],"description":"For a model built with options: how this unit is built. Set from the purchase order line it arrived on; recorded by hand for a unit that came another way. NULL when not recorded. An order line naming the unit takes it. null when they are not known."}},"additionalProperties":false,"examples":[{"notes":"Scratch on the north corner, disclosed to the buyer."},{"options":[{"group":"Shell color","option":"Sterling Marble"}]}]},"TransferStockInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"from_location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"to_location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are moved. Exactly 1 for a serialized unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"serialized_unit_id":{"description":"The individual unit moved. Required for a serial-tracked item, not allowed otherwise.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot to move. Left out, the first lots to expire move, then the oldest. They arrive in the same lots. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"staff_member_id":{"description":"Who moved it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why it was moved."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["item_id","from_location_id","to_location_id","quantity","idempotency_key"],"additionalProperties":false,"examples":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","from_location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","to_location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","quantity":"6","note":"Restock Hailey for the weekend","idempotency_key":"0199a3c4-restock-hailey-1003"}]},"CreateStockCountInput":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who is responsible for the count. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"notes":{"type":"string","minLength":1,"maxLength":1000,"description":"Free-form notes: what was counted and why."},"covers_units":{"default":false,"description":"True when the count covers the serialized units at its location (spas, swim spas): each is scanned by its serial number, and posting reconciles every unit recorded here, so one not scanned is missing. One such count is open at a location at a time. False: serialized units are not counted and not touched. Fixed once the count is started.","type":"boolean"},"lines":{"default":[],"maxItems":500,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"counted_quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many were found on the shelf. A decimal string with up to 3 decimal places, for example \"12.500\"."},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot counted, each lot on a line of its own. Left out, the line counts the unknown lot: stock whose lot is not known. A code not yet recorded for the item is recorded as a new lot. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"expires_on":{"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire. Recorded on a new lot, or on a lot that has none yet.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["item_id","counted_quantity"],"additionalProperties":false},"description":"What was found on the shelf. Counting an item (or a lot of it) again replaces what was recorded for it."}},"required":["location_id"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","notes":"Chemical wall, quarterly cycle count"},{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","notes":"Monthly spa count for the floor-plan audit","covers_units":true}]},"RecordStockCountInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"counted_quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many were found on the shelf. A decimal string with up to 3 decimal places, for example \"12.500\"."},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot counted, each lot on a line of its own. Left out, the line counts the unknown lot: stock whose lot is not known. A code not yet recorded for the item is recorded as a new lot. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"expires_on":{"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire. Recorded on a new lot, or on a lot that has none yet.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["item_id","counted_quantity"],"additionalProperties":false},"description":"What was found on the shelf. Counting an item (or a lot of it) again replaces what was recorded for it."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","counted_quantity":"17"}]}]},"ExplainStockCountInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"stock_lot_id":{"description":"For an item tracked by lot: the lot's line, or null for the line of its unknown lot. Left out, every line of the item.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]},"adjustment_reason_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"Why the item was found over or short, when whoever posts the count knows: theft, breakage. NULL puts the difference down to the business's count reason (count error, by default). Set only while the count is open. By the reason's code or id; null puts it back to the count reason. Accepts the record's id or its reason code.","x-natural-key":"code"}},"required":["item_id","adjustment_reason_id"],"additionalProperties":false},"description":"Why each item was over or short."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","adjustment_reason_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]}]},"RecordStockCountUnitsInput":{"type":"object","properties":{"units":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"serial_number":{"type":"string","minLength":1,"maxLength":100,"description":"The serial number read off the unit's data plate."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."}},"required":["serial_number"],"additionalProperties":false},"description":"The serial numbers found at the location. Scanning one again takes afresh where the system has it. A serial no unit has is recorded as not known, never created."}},"required":["units"],"additionalProperties":false,"examples":[{"units":[{"serial_number":"GR26-0301"},{"serial_number":"VG26-0311"}]}]},"WriteDownSerializedUnitInput":{"type":"object","properties":{"cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the unit is worth now: its new cost. Must be less than its cost today. A decimal string with up to 2 decimal places, for example \"12.50\"."},"adjustment_reason_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its reason code."},"note":{"description":"What happened, in words. Required when the reason asks for one.","type":"string","minLength":1,"maxLength":1000},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["cost","adjustment_reason_id","idempotency_key"],"additionalProperties":false,"examples":[{"cost":"7800.00","adjustment_reason_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","note":"Showroom display model since March, cover faded","idempotency_key":"0199a3c4-writedown-hs26-004471"}]},"UpdateStockLotInput":{"type":"object","properties":{"lot_code":{"type":"string","minLength":1,"maxLength":100,"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known."},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire."}},"additionalProperties":false,"examples":[{"expires_on":"2027-06-30"},{"lot_code":"L2309A"}]},"CreateStockAdjustmentReasonInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Z0-9][A-Z0-9_-]{0,29}$","description":"A short code for the reason, unique within the business, uppercase letters, digits, _ and -: BROKEN, THEFT. Fixed once the reason exists."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"The reason as staff choose it, for example \"Broken or spilled\"."},"ledger_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The expense account what is lost for this reason posts to, for example Store Supplies for goods used in the showroom; what is found is credited there. NULL (the default) posts to Inventory Adjustments. Only an expense account the system does not post its own events to. A change applies to stock moved from then on. Accepts the record's id or its account code.","x-natural-key":"code"},"note_required":{"type":"boolean","description":"True when each adjustment for this reason must also say what happened in its note, for example \"Other\". False (the default) lets the reason speak for itself."},"is_count_reason":{"type":"boolean","description":"True for the one reason a stock count's differences are put down to when no other is given: count error, by default. At most one per business, and only an active one. With none, a count's differences carry no reason. Making one the count reason makes the one before it not."}},"required":["code","name"],"additionalProperties":false,"examples":[{"code":"DONATED","name":"Donated to a charity raffle","note_required":true}]},"UpdateStockAdjustmentReasonInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The reason as staff choose it, for example \"Broken or spilled\"."},"ledger_account_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The expense account what is lost for this reason posts to, for example Store Supplies for goods used in the showroom; what is found is credited there. NULL (the default) posts to Inventory Adjustments. Only an expense account the system does not post its own events to. A change applies to stock moved from then on. Accepts the record's id or its account code.","x-natural-key":"code"},"note_required":{"type":"boolean","description":"True when each adjustment for this reason must also say what happened in its note, for example \"Other\". False (the default) lets the reason speak for itself."},"is_count_reason":{"type":"boolean","description":"True for the one reason a stock count's differences are put down to when no other is given: count error, by default. At most one per business, and only an active one. With none, a count's differences carry no reason. Making one the count reason makes the one before it not."},"is_active":{"type":"boolean","description":"False once the reason is no longer used: no new adjustment can name it. Stock already moved for it keeps it."}},"additionalProperties":false,"examples":[{"name":"Broken on the shelf"},{"is_active":false}]},"SetParLevelsInput":{"type":"object","properties":{"levels":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"par_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The quantity the location is brought back up to: order up to. NULL once the location no longer keeps a level for the item. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The quantity the location is brought back up to: order up to. NULL once the location no longer keeps a level for the item. Send null to stop keeping a level of the item there."},"minimum_quantity":{"description":"The reorder point: when what the location holds, with what is on order and less what is promised to customers, is at or below this, bring it back up to par. NULL: bring it back as soon as it is below par. Less than par_quantity. Left out, the level has no minimum.","anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The reorder point: when what the location holds, with what is on order and less what is promised to customers, is at or below this, bring it back up to par. NULL: bring it back as soon as it is below par. Less than par_quantity. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}]}},"required":["item_id","location_id","par_quantity"],"additionalProperties":false},"description":"The levels to set, each an item at a location."}},"required":["levels"],"additionalProperties":false,"examples":[{"levels":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","par_quantity":"2","minimum_quantity":"1"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","par_quantity":"6"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","par_quantity":null}]}]},"TransferStockBatchInput":{"type":"object","properties":{"from_location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"to_location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are moved. Exactly 1 for a serialized unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"serialized_unit_id":{"description":"The individual unit moved. Required for a serial-tracked item, not allowed otherwise.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot to move. Left out, the first lots to expire move, then the oldest. They arrive in the same lots. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["item_id","quantity"],"additionalProperties":false},"description":"Each item to move. An item appears once, unless its lines name different units or lots."},"staff_member_id":{"description":"Who moved it. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why it was moved."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["from_location_id","to_location_id","lines","idempotency_key"],"additionalProperties":false,"examples":[{"from_location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","to_location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","quantity":"4"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"2"}],"note":"Monday restock","idempotency_key":"0199a3c4-restock-van1-1006"}]},"CreateLocationInput":{"type":"object","properties":{"branch_code":{"type":"string","pattern":"^[A-Z0-9]{2,8}$","description":"Short uppercase code staff use to name the location, unique within the tenant, for example BOI."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name, for example \"Boise Showroom\"."},"kind":{"default":"premises","type":"string","enum":["premises","vehicle"],"description":"premises: a showroom, warehouse or service base. vehicle: a service van or truck that carries stock; its stock is counted and moved like any location's, but nothing is sold, scheduled or staffed from it. Fixed once the location exists."},"time_zone":{"description":"IANA time zone, for example America/Boise. Business days and opening hours are evaluated in it. Required for premises; a vehicle takes its home's.","type":"string"},"home_location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"driver_staff_member_id":{"description":"For a vehicle: the technician who normally drives it, at most one active vehicle each. Parts that technician records as used on a job at a customer's site come from this vehicle unless another location is named. NULL for premises.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"Street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"City or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"State or province code, for example ID."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"ZIP or postal code."},"address_country":{"default":"US","type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Public phone number in E.164 form, for example +12085550100."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the location is in. Sales the customer carries out of this location are taxed by it. NULL for a vehicle: nothing is carried out of a van."}},"required":["branch_code","name"],"additionalProperties":false,"examples":[{"branch_code":"BOI","name":"Boise Showroom","time_zone":"America/Boise"},{"branch_code":"VAN2","name":"Service van 2","kind":"vehicle","home_location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"branch_code":"TWF","name":"Twin Falls Showroom","time_zone":"America/Boise","street_address":"1563 Fillmore St","address_locality":"Twin Falls","address_region":"ID","postal_code":"83301","telephone":"+12085550100"}]},"UpdateLocationInput":{"type":"object","properties":{"branch_code":{"type":"string","pattern":"^[A-Z0-9]{2,8}$","description":"Short uppercase code staff use to name the location, unique within the tenant, for example BOI."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name, for example \"Boise Showroom\"."},"time_zone":{"type":"string","description":"IANA time zone, for example America/Boise. Business days and opening hours are evaluated in it."},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"Street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"City or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"State or province code, for example ID."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"ZIP or postal code."},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Public phone number in E.164 form, for example +12085550100."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the location is in. Sales the customer carries out of this location are taxed by it. NULL for a vehicle: nothing is carried out of a van."},"is_active":{"type":"boolean","description":"False once the location has closed. Closed locations stay for history and are never deleted."},"home_location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"For a vehicle: the premises it works from and is restocked from, which must be open. A role held at that location holds for the vehicle too. Its time zone is the vehicle's. NULL for premises. Accepts the record's id or its branch code.","x-natural-key":"branch_code"},"driver_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a vehicle: the technician who normally drives it, at most one active vehicle each. Parts that technician records as used on a job at a customer's site come from this vehicle unless another location is named. NULL for premises."},"price_list_id":{"anyOf":[{"type":"string","x-natural-key":"code"},{"type":"null"}],"description":"The store's regular price list: its entries set what items sell for at this store, and an item with no entry on it sells at its own price. Stores naming the same list share one regional price. NULL: every item sells at its own price. Only a regular list can be named here; an offer list (a trade, employee or contract price, or a sale) competes with the regular price wherever it applies. Changing it needs price_list.manage. Orders already written keep their prices: repriceSalesOrder brings a quote up to date. Accepts its id or its code. Accepts the record's id or its price list code.","x-natural-key":"code"}},"additionalProperties":false,"examples":[{"name":"Boise Showroom and Service"},{"telephone":null,"is_active":false}]},"CreatePriceListInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Z0-9][A-Z0-9_-]{0,15}$","description":"Short uppercase code staff and order lines name the list by, for example CONTR or WOODRIVER. Unique within the tenant."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name, for example \"Contractor\" or \"Wood River stores\"."},"description":{"type":"string","minLength":1,"maxLength":1000,"description":"What the list is for and who gets it, for staff."},"kind":{"type":"string","enum":["regular","offer"],"description":"regular: a store's or region's own prices, named by stores in location.price_list_id; fixed prices by item or pack only. offer: competes with the regular price wherever it applies, the lowest winning; prices or percents off, by item, pack or category. Fixed once the list exists."},"audience":{"default":"everyone","description":"Who an offer list is for. everyone: every sale where it applies, as a store-wide sale. assigned: only customers put on it (customer_price_list), as a trade, employee or contract price. Always everyone on a regular list. Defaults to everyone.","type":"string","enum":["everyone","assigned"]},"stores":{"maxItems":100,"type":"array","items":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."}},"required":["location_id"],"additionalProperties":false},"description":"For an offer list: the stores it is limited to. Empty or left out applies it at every store. On an update, the whole set: stores left out no longer have it. A regular list is given to a store with updateLocation (price_list_id)."}},"required":["code","name","kind"],"additionalProperties":false,"examples":[{"code":"CONTR","name":"Contractor","kind":"offer","audience":"assigned"},{"code":"WOODRIVER","name":"Wood River stores","kind":"regular"},{"code":"SPRINGSALE","name":"Spring chemical sale","kind":"offer","stores":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]}]},"UpdatePriceListInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Z0-9][A-Z0-9_-]{0,15}$","description":"Short uppercase code staff and order lines name the list by, for example CONTR or WOODRIVER. Unique within the tenant."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name, for example \"Contractor\" or \"Wood River stores\"."},"description":{"description":"What the list is for and who gets it, for staff.","anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What the list is for and who gets it, for staff."},{"type":"null"}]},"audience":{"description":"Who an offer list is for. everyone: every sale where it applies, as a store-wide sale. assigned: only customers put on it (customer_price_list), as a trade, employee or contract price. Always everyone on a regular list.","type":"string","enum":["everyone","assigned"]},"is_active":{"description":"False once the list is retired: it prices no new line, and lines already priced from it keep their prices.","type":"boolean"},"stores":{"maxItems":100,"type":"array","items":{"type":"object","properties":{"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."}},"required":["location_id"],"additionalProperties":false},"description":"For an offer list: the stores it is limited to. Empty or left out applies it at every store. On an update, the whole set: stores left out no longer have it. A regular list is given to a store with updateLocation (price_list_id)."}},"additionalProperties":false,"examples":[{"is_active":false},{"stores":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]}]},"SetPriceListPricesInput":{"type":"object","properties":{"prices":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"item_category_id":{"description":"For an offer list only: prices every item filed directly under this category (not its subcategories), as a percent off or a fixed price. NULL when the entry is for an item. Send this or item_id.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"pack_text":{"description":"For a price on one of the item's packs (case, bag): the pack, as the item's pack is called. The price is then for one pack, and min_quantity counts packs. NULL prices the item's own unit, and a pack of it at that unit price times the pack size.","type":"string","minLength":1,"maxLength":50},"min_quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The smallest quantity on one line the price applies to: a quantity break. In packs for a pack's entry, otherwise in the item's unit. 0 (the default) applies to any quantity. Lines are not added together: each line is priced on its own quantity. schema.org: eligibleQuantity. A decimal string with up to 3 decimal places, for example \"12.500\"."},"price":{"description":"The price of one unit (or one pack) before tax, options added on. NULL when the entry is a percent off. Send this or percent_off.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"percent_off":{"description":"For an offer list only: the percent taken off the regular price (with any options), rounded to the cent per unit. NULL when the entry is a fixed price. Send this or price.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"valid_from":{"description":"The first order date the price applies to. The order's date, which also picks its tax rates, decides. Defaults to today.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"additionalProperties":false},"description":"The prices to put on the list. Each is keyed by what it prices (item, pack or category) and its min_quantity: one already in force before its valid_from is ended the day before; one starting that same day is replaced; one starting later stays, and the new price runs until it."}},"required":["prices"],"additionalProperties":false,"examples":[{"prices":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","price":"18.50","valid_from":"2026-11-01"}]},{"prices":[{"item_category_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","percent_off":"10"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","pack_text":"case","min_quantity":"2","price":"95.00"}]}]},"EndPriceListPricesInput":{"type":"object","properties":{"price_list_entry_ids":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"description":"The entries to end, as getPriceList shows them."},"valid_through":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last order date they apply to. An entry cannot end before it starts."}},"required":["price_list_entry_ids","valid_through"],"additionalProperties":false,"examples":[{"price_list_entry_ids":["0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"],"valid_through":"2026-12-31"}]},"AssignCustomerPriceListInput":{"type":"object","properties":{"price_list_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its price list code."},"valid_from":{"description":"The first order date the list prices for them. Defaults to today.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"valid_through":{"description":"The last order date the list prices for them. NULL: until ended. The day before valid_from for a place ended before it began.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"The contract or account number the price rests on, for staff and the customer."}},"required":["price_list_id"],"additionalProperties":false,"examples":[{"price_list_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"price_list_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","valid_from":"2026-10-01","reference":"Contract 2026-114"}]},"EndCustomerPriceListInput":{"type":"object","properties":{"valid_through":{"description":"The last order date the list prices for them. Leave it out to end it at once: yesterday, so today's orders no longer take it.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"additionalProperties":false,"examples":[{},{"valid_through":"2026-12-31"}]},"AddLandedCostInput":{"type":"object","properties":{"description":{"type":"string","minLength":1,"maxLength":500,"description":"What was paid for and to whom, for example \"Freight, ABC Carriers bill 5521\"."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What was paid, before it is shared out. A decimal string with up to 2 decimal places, for example \"12.50\"."},"basis":{"default":"value","description":"How it is shared among the arrivals: value (in proportion to what each cost) or quantity (in proportion to how many each brought; a serialized unit is one). Default value.","type":"string","enum":["value","quantity"]},"purchase_receipt_ids":{"description":"The arrivals it is shared among, from the purchase order's receipts. NULL for every arrival on the order. Send this or serialized_unit_ids, or neither.","minItems":1,"maxItems":500,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."}},"serialized_unit_ids":{"description":"The serialized units it is shared among, for a crane or prep on particular units: each unit's latest arrival on this order.","minItems":1,"maxItems":500,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."}},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["description","amount","idempotency_key"],"additionalProperties":false,"examples":[{"description":"Freight, ABC Carriers bill 5521","amount":"1200.00","idempotency_key":"0199a3c4-freight-0001"},{"description":"Crane to set the swim spa","amount":"450.00","serialized_unit_ids":["0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"],"ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","idempotency_key":"0199a3c4-crane-0001"}]},"CreatePurchaseOrderInput":{"type":"object","properties":{"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor ordered from. Fixed once the order has lines."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"expected_on":{"description":"The date the vendor says the goods will arrive.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff."},"lines":{"default":[],"description":"What to order. Costs default to the vendor's on record.","maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many are ordered, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs are ordered: quantity is packs times pack_size. Send it instead of quantity to order by the vendor's pack. NULL on a line ordered in units, or whose quantity is not a whole number of packs."},"pack_text":{"description":"For a line ordered in the vendor's pack: the pack, as on the vendor's offer (case, pallet). NULL for a line ordered in the item's own unit. Fixed once the line exists. Optional: the vendor's pack on record is the one used.","type":"string","minLength":1,"maxLength":40},"pack_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The cost of one pack agreed with the vendor. Defaults to the vendor's pack cost on record, or its unit cost times pack_size. Fixed once goods have arrived on the line. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"unit_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The cost per unit agreed with the vendor. Defaults to the cost on record for this vendor. On a line ordered by the pack it is pack_cost divided by pack_size, to four places. Calculated. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"vendor_sku":{"type":"string","minLength":1,"maxLength":100,"description":"The vendor's own code for the item. Defaults to the one on record for this vendor."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says. Defaults to the item's name."},"sales_order_line_id":{"description":"For a special order: the customer's order line these goods are being bought for. It must be a line for the same item, on a sales order that is open.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the factory is to build it. Required for a model that has options. On a special order it is the customer's order line's, and follows that line while nothing has arrived. A unit received on the line is recorded as built so. Left out on a special order, the customer's line's options are used. Each option's cost difference is added to the cost unless unit_cost is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false}}},"required":["vendor_id","location_id"],"additionalProperties":false,"examples":[{"vendor_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","expected_on":"2026-11-01","lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","quantity":"48"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","packs":10}]}]},"UpdatePurchaseOrderInput":{"type":"object","properties":{"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor ordered from. Fixed once the order has lines."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the vendor says the goods will arrive. Every line still to come moves to it, each move kept in the line's date_changes."},"date_change_note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the date moved, as the vendor said it, for whoever answers the customer next. Sent with expected_on."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff."},{"type":"null"}],"description":"Free-form notes for staff."}},"additionalProperties":false,"examples":[{"expected_on":"2026-11-08","date_change_note":"Factory says one week late."}]},"AddPurchaseOrderLinesInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"description":"How many are ordered, in the item's unit. 0 drops the line. Send this or packs.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs are ordered: quantity is packs times pack_size. Send it instead of quantity to order by the vendor's pack. NULL on a line ordered in units, or whose quantity is not a whole number of packs."},"pack_text":{"description":"For a line ordered in the vendor's pack: the pack, as on the vendor's offer (case, pallet). NULL for a line ordered in the item's own unit. Fixed once the line exists. Optional: the vendor's pack on record is the one used.","type":"string","minLength":1,"maxLength":40},"pack_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The cost of one pack agreed with the vendor. Defaults to the vendor's pack cost on record, or its unit cost times pack_size. Fixed once goods have arrived on the line. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"unit_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The cost per unit agreed with the vendor. Defaults to the cost on record for this vendor. On a line ordered by the pack it is pack_cost divided by pack_size, to four places. Calculated. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"vendor_sku":{"type":"string","minLength":1,"maxLength":100,"description":"The vendor's own code for the item. Defaults to the one on record for this vendor."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says. Defaults to the item's name."},"sales_order_line_id":{"description":"For a special order: the customer's order line these goods are being bought for. It must be a line for the same item, on a sales order that is open.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"options":{"description":"For a model built with options: how the factory is to build it. Required for a model that has options. On a special order it is the customer's order line's, and follows that line while nothing has arrived. A unit received on the line is recorded as built so. Left out on a special order, the customer's line's options are used. Each option's cost difference is added to the cost unless unit_cost is sent.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}}},"required":["item_id"],"additionalProperties":false},"description":"The lines to add."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"12","unit_cost":"18.2500"}]}]},"UpdatePurchaseOrderLineInput":{"type":"object","properties":{"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are ordered, in the item's unit. 0 drops the line. A decimal string with up to 3 decimal places, for example \"12.500\"."},"packs":{"type":"integer","minimum":0,"maximum":1000000,"description":"How many packs are ordered: quantity is packs times pack_size. Send it instead of quantity to order by the vendor's pack. NULL on a line ordered in units, or whose quantity is not a whole number of packs."},"unit_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The cost per unit agreed with the vendor. Defaults to the cost on record for this vendor. On a line ordered by the pack it is pack_cost divided by pack_size, to four places. Calculated. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"pack_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The cost of one pack agreed with the vendor. Defaults to the vendor's pack cost on record, or its unit cost times pack_size. Fixed once goods have arrived on the line. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"vendor_sku":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The vendor's own code for the item. Defaults to the one on record for this vendor."},{"type":"null"}],"description":"The vendor's own code for the item. Defaults to the one on record for this vendor."},"description":{"type":"string","minLength":1,"maxLength":500,"description":"What the line says. Defaults to the item's name."},"options":{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false},"description":"For a model built with options: how the factory is to build it. Required for a model that has options. On a special order it is the customer's order line's, and follows that line while nothing has arrived. A unit received on the line is recorded as built so. Not on a special order: change the options on the customer's line, which the line follows."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The vendor's new date for this line's goods still to come (null: it gives none), kept in its date_changes. When every line still to come is then due the same day, the order's expected_on becomes that day."},"date_change_note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the date moved, as the vendor said it, for whoever answers the customer next. Sent with expected_on."}},"additionalProperties":false,"examples":[{"quantity":"60"},{"quantity":"0"},{"packs":12},{"expected_on":"2026-11-20","date_change_note":"Factory pushed the build two weeks."}]},"ReceivePurchaseOrderInput":{"type":"object","properties":{"lines":{"description":"What arrived. Omit to receive everything still expected on lines that are not serial-tracked: a line ordered by the pack arrives in whole packs where what is left is whole packs.","maxItems":200,"type":"array","items":{"type":"object","properties":{"purchase_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The line the goods are for."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many arrived. For a serial-tracked item, leave it out and send serial_numbers. A decimal string with up to 3 decimal places, for example \"12.500\"."},"packs":{"type":"integer","minimum":1,"maximum":1000000,"description":"On a line ordered by the pack: how many whole packs arrived, instead of quantity. They go into stock at exactly pack_cost each."},"pack_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"For goods received by the pack: the actual cost of one pack, when the vendor's invoice differs from the order. Defaults to the line's pack_cost. NULL for goods counted in units and for goods sent back. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"serial_numbers":{"description":"For a serial-tracked item: the serial number of each unit that arrived.","maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":100}},"unit_cost":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The actual cost per unit, when the vendor's invoice differs from the order. Defaults to the line's cost. Ignored for goods sent back, which are credited at what they were received at. For goods received by the pack: pack_cost divided by the pack size, to four places. Calculated. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"options":{"description":"For serial-tracked units that arrived built otherwise than the line asked (another shell color, say): how they are built. Left out, they are recorded as built as ordered.","minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":100,"description":"The option group, by name (case does not matter) or id, for example Shell color."},"option":{"type":"string","minLength":1,"maxLength":100,"description":"The option chosen in it, by name (case does not matter) or id, for example Sterling Marble."}},"required":["group","option"],"additionalProperties":false}},"floor_plan_lender_id":{"description":"For serial-tracked units bought on a floor plan: the lender that paid the vendor for them. Each unit goes on its floor plan at what it was received at, dated today. Needs ledger.post.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot that arrived, as printed on the goods. Required unless the business lets stock of an unknown lot in; a delivery of two lots is two lines. A code not yet recorded for the item is recorded as a new lot. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"expires_on":{"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire. Recorded on a new lot, or on a lot that has none yet.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["purchase_order_line_id"],"additionalProperties":false}},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who received or sent the goods. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Anything worth recording: damage on arrival, why goods went back."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["idempotency_key"],"additionalProperties":false,"examples":[{"idempotency_key":"0199a3c4-po-2201-arrived"},{"lines":[{"purchase_order_line_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","serial_numbers":["HS26-004471","HS26-004472"]}],"note":"Crate 2 has a cracked corner","idempotency_key":"0199a3c4-po-2202-tubs"}]},"ReturnToVendorInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"purchase_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The line the goods are for."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many go back. For a serial-tracked item, send serialized_unit_ids instead. A decimal string with up to 3 decimal places, for example \"12.500\"."},"packs":{"type":"integer","minimum":1,"maximum":1000000,"description":"On a line ordered by the pack: how many whole packs go back, instead of quantity."},"serialized_unit_ids":{"description":"For a serial-tracked item: each unit going back.","maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."}},"vendor_credit_expected":{"description":"What the vendor said it will credit for this line's goods, in all. For goods sent back: what the vendor said it will credit for them, when that is known: less than extended_cost when it keeps a restocking fee. Recorded for whoever enters the vendor's credit memo in the accounting system; nothing posts from it, and what is owed comes down by what the goods were received at. NULL when not given, and on goods received. Shared among the units when several go back.","type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot going back. Required. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"written_off_stock_movement_id":{"description":"For goods not tracked by serial number that were already written off as a loss: that loss. For goods sent back that were already taken out of stock as a loss (a case damaged in freight, written off when it arrived): that loss, an adjustment out of the same item at the location they go back from. The goods are put back on the books first, undoing that much of the loss (restored_stock_movement_id), so they leave stock once, as a return to the vendor. A loss is sent back at most up to what it took out. A serial-tracked unit written off is found by the system. NULL for goods still in stock.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["purchase_order_line_id"],"additionalProperties":false},"description":"What goes back to the vendor."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who received or sent the goods. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"vendor_rma_number":{"description":"For goods sent back: the vendor's return authorization (RMA) number, as the vendor gave it, to match their credit memo against. NULL when none was given, and on goods received.","type":"string","minLength":1,"maxLength":100},"note":{"description":"Why the goods go back.","type":"string","minLength":1,"maxLength":1000},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["lines","idempotency_key"],"additionalProperties":false,"examples":[{"lines":[{"purchase_order_line_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"2","vendor_credit_expected":"36.50"}],"vendor_rma_number":"RMA-448812","note":"Leaking buckets","idempotency_key":"0199a3c4-po-2201-rtv"}]},"ReturnGoodsWithoutReceiptInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many came back, in the item's unit. More than 0. A decimal string with up to 3 decimal places, for example \"12.500\"."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"payment_method_id":{"description":"How the refund went out: a payment method, or the Store credit method for credit to the customer. Defaults to Store credit when the business gives only store credit for these returns. Required unless the business gives only store credit for these returns.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"reference":{"type":"string","minLength":1,"maxLength":100,"description":"For a refund by check: the number of the check the business wrote, so the bank statement can be matched. Needed while the business requires check numbers (tenant_policy.check_number_required). A note of the method's own reference otherwise, or NULL."},"cash_drawer_session_id":{"type":"string","x-natural-key":"session_number","description":"Accepts the record's id or its drawer number (as a number or a string of digits)."},"restock":{"description":"True when the goods go back into stock to sell again, at the location's average cost. False when they are thrown away or unsellable: nothing comes into stock and the cost of the original sale stands. Always false for an item whose stock is not tracked. Defaults to true.","type":"boolean"},"lot_code":{"description":"For an item tracked by lot (lot_tracked): the lot of the goods coming back, as printed on them. Required for goods restocked, unless the business lets stock of an unknown lot in. A code not yet recorded for the item is recorded as a new lot. Its code as printed on the goods, matched within the line's item.","type":"string","minLength":1,"maxLength":100},"stock_lot_id":{"description":"The lot by its id instead of its code, from listStockLots. The unknown lot is named only this way.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"expires_on":{"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire. Recorded on a new lot, or on a lot that has none yet.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the goods came back and why no sale could be found for them. Required."},"staff_member_id":{"description":"Who took the goods back. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["item_id","quantity","location_id","note","idempotency_key"],"additionalProperties":false,"examples":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"1","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","note":"Unopened shock, bought for cash last month; no receipt","idempotency_key":"0199a3c4-noreceipt-boi-0412"}]},"CreatePaymentMethodInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The method's name as staff see it, unique within the tenant."},"kind":{"type":"string","enum":["cash","check","card","financing","other"],"description":"What sort of method it is: cash, check, card, financing (a lender pays the tenant), other, store_credit, account_credit or gift_card. store_credit is the one method the system makes for paying with a customer's store credit and refunding into it: it posts to the Store Credit account and moves the customer's balance. account_credit (\"On account\") is the one the system makes for paying an order out of money the customer paid on account, and for moving money held on an order back into their account credit: it posts to Customer Payments on Account. gift_card is the one the system makes for paying with a gift card and refunding onto one, naming the card: it posts to Gift Cards Outstanding and moves the card's balance. trade_in (\"Trade-in\") is the one the system makes for what a customer is allowed for an old unit they trade in toward an order: it posts to Trade-Ins Clearing, and only receiving or giving back the trade (receiveTradeIn, giveBackTradeIn) pays or refunds by it. None of them can be added, and no other method can become one."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."}},"required":["name","kind","ledger_account_id"],"additionalProperties":false,"examples":[{"name":"Wells Fargo financing","kind":"financing","ledger_account_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"UpdatePaymentMethodInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The method's name as staff see it, unique within the tenant."},"kind":{"type":"string","enum":["cash","check","card","financing","other"],"description":"What sort of method it is: cash, check, card, financing (a lender pays the tenant), other, store_credit, account_credit or gift_card. store_credit is the one method the system makes for paying with a customer's store credit and refunding into it: it posts to the Store Credit account and moves the customer's balance. account_credit (\"On account\") is the one the system makes for paying an order out of money the customer paid on account, and for moving money held on an order back into their account credit: it posts to Customer Payments on Account. gift_card is the one the system makes for paying with a gift card and refunding onto one, naming the card: it posts to Gift Cards Outstanding and moves the card's balance. trade_in (\"Trade-in\") is the one the system makes for what a customer is allowed for an old unit they trade in toward an order: it posts to Trade-Ins Clearing, and only receiving or giving back the trade (receiveTradeIn, giveBackTradeIn) pays or refunds by it. None of them can be added, and no other method can become one."},"ledger_account_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its account code."},"is_active":{"type":"boolean","description":"False once the method is no longer offered."}},"additionalProperties":false,"examples":[{"is_active":false}]},"UpdatePolicyInput":{"type":"object","properties":{"inventory_costing_method":{"type":"string","enum":["average","fifo"],"description":"How the cost of stock leaving is worked out: average (running average cost of what is on hand) or fifo (oldest receipts first). It should match the method the business reports to its accountant. Fixed once any stock has moved."},"allow_negative_stock":{"type":"boolean","description":"True to let a quantity-tracked item be issued when the system shows too little on hand, so a sale is never blocked by late paperwork. False to refuse it. Either way a transfer or a return to a vendor carries only what the location holds, and serialized units can never go negative."},"unknown_lot_allowed":{"type":"boolean","description":"For items tracked by lot: true to let stock come in (received, opening stock, an adjustment or a count) without naming its lot, into the item's unknown lot. False to refuse it, so every unit on the shelf can be traced in a recall. Goods coming back (a customer's return, a transfer) always come back into the lot they left from."},"stock_count_blind":{"type":"boolean","description":"True (the default): whoever counts stock is not shown what the system holds of each item, nor how far over or short their count is, until the count is posted, so they count what is there rather than confirm a figure. Someone holding purchase.manage at the location may choose to see those figures: they read what is on hand to decide what to buy, so hiding it from them hides nothing. False: everyone counting sees them. The API applies it to the count itself: while a blind count is open, getStockCount leaves out what the system held of each item and which serialized units it expects, unless the key holds purchase.manage at the location. What is on hand stays readable through listStock, which selling needs."},"stock_count_missing_units":{"type":"string","enum":["flag","write_off"],"description":"What posting a stock count that covers serialized units does with a unit recorded at the location that was not scanned. flag (the default): the unit stays in stock and is marked missing on the count, for someone to find: a spa not scanned is far more often on a truck, at another store or behind the building than gone, and writing it off moves its whole cost to inventory adjustments. write_off: the unit leaves stock at its cost, as a count's shortage does. A unit an order or quote holds for a customer is only ever flagged: it may be out for delivery."},"par_level_replenishment":{"type":"string","enum":["with_pace","par_only"],"description":"How getReplenishment weighs an item's par level at a location against how fast it sells there. with_pace (the default): it suggests whichever asks for more, so a slow mover that must always be on the shelf is brought back up to par when it falls to its minimum, and a fast seller is still ordered for the days ahead when its pace outruns the par. par_only: where an item has a par level, the par level alone decides, so a buyer who has set the shelf's levels is never asked for more. Items with no par level are ordered by pace either way. A vehicle is always restocked to its par levels alone."},"books_closed_through":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last date of the closed accounting period. Nothing can be posted on or before it, so figures already reported or exported cannot change. NULL when no period is closed. Changing it needs books.close."},"new_customer_credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The tenant's choice of the credit limit a new customer starts with when none is given. NULL (the default) starts them with no limit, as before limits existed; a figure such as 0, which has everything paid for before it is handed over, makes credit something staff grant. Starting a customer with more, or with none when this is set, needs credit.manage. Changing it needs credit.manage."},"tax_rounding":{"type":"string","enum":["line","order"],"description":"The tenant's choice of how sales tax is rounded to the cent, for each jurisdiction. line (the default): each line's tax is rounded on its own and the order's tax is their sum. order: the tax on the whole order is rounded once, and shared out to its lines so that they add up to it; with prices like 9.99 this can come to a cent less than rounding each line. Use what the tenant's states require or allow. An order keeps the rounding in force when it was written up (sales_order.tax_rounding)."},"tax_rate_date":{"type":"string","enum":["order_date","hand_over"],"description":"The tenant's choice of which day's tax rates a sale is charged at, which each state's law decides. order_date (the default): the rates in force on the order's date, so a tub ordered before a rate change and delivered after it keeps the old rate. hand_over: the rates in force on the day the goods are handed over; until then an order shows today's rates, and reading the order, a hand-over or a payment brings the lines not yet handed over to that day's rates, so a deposit taken against the old total is weighed against the new one. A line partly handed over keeps the rates of its first hand-over for the rest of it. An order keeps the choice in force when it was written up (sales_order.tax_rate_date)."},"carry_out_elsewhere":{"type":"string","enum":["refuse","tax_at_pickup"],"description":"The tenant's choice of what happens when goods the customer carries out (an order with no delivery site) are handed over at a location whose tax area would tax the order differently from the order's own. refuse (the default): the hand-over is refused, and the goods are brought to a location that taxes them the same, or the order is moved. tax_at_pickup: the order is taxed again in the tax area of the location handing it over, as long as none of it has yet been handed over; only from a location that holds the goods handed over, and never for a service, which is not carried out of anywhere; until then its tax area may be moved to a location's tax area without tax.override when that does not lower the tax, and what was already paid stays as a deposit against the new total. Areas that charge the same jurisdictions at the same rates on the order's items count as the same either way."},"exemption_without_certificate":{"type":"string","enum":["charge_tax","exempt"],"description":"What happens when a tax-exempt customer buys and no certificate of theirs covers the sale (none on file, expired, not yet in force, or good in another state). charge_tax (the default): the sale is taxed, and says why, until a certificate that covers it is recorded or someone holding tax.override exempts it. exempt: the sale is untaxed anyway, naming no certificate; listTaxExemptionCertificates shows whose to chase."},"new_customer_payment_terms_days":{"type":"integer","minimum":0,"maximum":365,"description":"The payment terms a new customer starts with when none are given, in days after hand-over: 0 is due on hand-over, 30 is \"net 30\". Changing it, or starting a customer on longer terms, needs credit.manage."},"hold_when_overdue_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":365},{"type":"null"}],"description":"Holds goods and service on account from a customer who has anything more than this many days overdue: a hand-over that would add to what they owe is refused, while one paid for in full still goes through. 0 holds from the first day overdue. NULL holds nobody; the credit limit alone applies. Changing it needs credit.manage."},"finance_charges":{"type":"boolean","description":"True when the business charges customers for paying late: assessFinanceCharges then charges each customer, once a month, on what they owe past due. False by default, so nobody is charged until the business decides to, at a rate (finance_charge_monthly_percent) or a flat amount (finance_charge_minimum) it chooses. Charges already made are still owed when it is turned off. Changing it needs credit.manage."},"finance_charge_monthly_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The finance charge, as a percent a month of what is past due: 1.5 is 18% a year. It is charged by the day, at this times 12 over 365 for each day an amount was past due in the month. 0 (the default) charges no rate: only finance_charge_minimum, a flat late fee. At most 10. Changing it needs credit.manage. A decimal string with up to 3 decimal places, for example \"12.500\"."},"finance_charge_minimum":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least a customer is charged in a month when they are charged at all: with a rate, the larger of the two; with no rate, a flat monthly late fee. 0 (the default) charges no minimum. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"finance_charge_min_overdue":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A customer whose past-due amounts charged on add up to less than this is not charged that month, so a few dollars late never brings a minimum charge larger than itself. 0 (the default) charges any amount past due. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"finance_charge_grace_days":{"type":"integer","minimum":0,"maximum":365,"description":"How many days past due an amount must be, at the month's end, before it is charged on. 0 (the default) charges anything past due; 10 leaves an invoice 10 days late or less alone that month. Changing it needs credit.manage."},"finance_charge_compounds":{"type":"boolean","description":"True when an earlier finance charge still unpaid past its due date is charged on as well, as any other amount past due. False (the default): finance charges are never charged on. Changing it needs credit.manage."},"store_credit_offered":{"type":"boolean","description":"True when the business gives store credit: refunds to the Store credit payment method, goodwill credits, and returns without a receipt refunded as credit. False by default, so nothing becomes owed as credit until the business decides it should. Credit already given can always be spent, and a refund can always put back what an order was paid in store credit, whatever this says."},"receiptless_returns":{"type":"string","enum":["refused","store_credit","any_tender"],"description":"Whether goods are taken back when no sale for them can be found. refused (the default): never; the goods go back only on the order they were sold on. store_credit: taken back for store credit only, which needs store_credit_offered and a named customer. any_tender: refunded by any payment method, store credit included."},"receiptless_return_price_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":730},{"type":"null"}],"description":"What goods taken back without a receipt are worth, per unit: the lowest the item sold for, after discounts, in this many days before the return, when that is below its price now. Goods given away for nothing do not count. NULL values them at the item's price now."},"receiptless_return_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most one return without a receipt can give back, tax included. NULL sets no limit."},"receiptless_return_refunds_tax":{"type":"boolean","description":"True to give back sales tax on goods taken back without a receipt, at the location's rates on the day. False (the default) gives back only their value: with no sale to point to, there is no tax charged to point to either."},"receiptless_return_permission":{"anyOf":[{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"]},{"type":"null"}],"description":"The permission a key must hold, at the location, to take goods back without a receipt. payment.refund by default. NULL lets anyone at the counter."},"cancellation_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of an order the business keeps, as a percent of its lines before tax, when an open order is cancelled before anything is handed over: the cancellation fee. Kept only out of money paid, never charged beyond it, and posted as fee income. 0 (the default) keeps nothing, so every payment is refunded before the order can be cancelled. A category's own cancellation_fee_percent overrides it for its items. To waive the fee, refund what was paid before cancelling. A decimal string with up to 2 decimal places, for example \"12.50\"."},"restocking_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of what goods returned on an order sold for, before tax, that the business keeps when they come back: the restocking fee, posted as fee income and kept from what is refunded (or owed, when the goods were not paid for). 0 (the default) keeps nothing. A category's own restocking_fee_percent overrides it for its items. Waiving it on a return needs order.discount where the goods come back. Goods taken back without a receipt are valued by their own rules and carry no fee. A decimal string with up to 2 decimal places, for example \"12.50\"."},"layaway_cancellation_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of a layaway the business keeps, as a percent of its lines before tax, when it is cancelled (the customer stops paying, or calls it off) before the goods go home: the layaway's forfeit or restocking fee. It takes the place of the cancellation fees for an order on layaway. Kept only out of money paid, never charged beyond it, as any cancellation fee. 0 (the default) keeps nothing: every payment is refunded before the layaway can be cancelled. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fees_taxed":{"type":"boolean","description":"True when the restocking and cancellation fees the business keeps are taxed, as some states tax a restocking fee: each fee is taxed in the order's tax area at the rates in force on the day it is kept, for the class of the goods it was kept on, and the tax is owed to each jurisdiction like any other sales tax. A cancellation fee is then kept tax included, out of what was paid. An order that is tax-exempt or has no tax area pays no tax on its fees. False (the default) taxes no fee."},"quote_valid_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":365},{"type":"null"}],"description":"How many days a new quote's prices hold when it is written up with no expires_on: expires_on is set to its order date plus this. NULL (the default) leaves a quote with no expiry date unless one is given."},"quote_holds_units":{"type":"boolean","description":"True (the default) when a quote that names a serialized unit holds it for the customer until the quote expires, is opened or is cancelled, so no one else can buy it. False when only an open order holds a unit: a quote names the unit it offers, but anyone may sell it meanwhile, and opening the quote is refused if it has gone. Turning it back on is refused while two quotes name the same unit."},"refund_to_original_method":{"type":"boolean","description":"True when a refund on an order goes back only by the methods the order was paid with, each up to what was paid by it less what has gone back by it: the card back to the card, cash back as cash. Refunding by another method then needs ledger.post where it is paid out. False (the default) lets a refund go out by any method. Store credit always goes back to store credit first, whatever this says."},"min_deposit_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least a customer must have paid, as a percent of the order's total with tax, before a quote is opened into an order or an order is written up open: the deposit that protects a special order placed with the factory. openSalesOrder can take the deposit in the same step. 0 (the default) needs nothing. A key holding credit.manage where the sale is made may open an order with less. Counter sales are paid in full, and orders made by subscriptions, work orders and sales channels are not held to it. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"below_cost_needs_approval":{"type":"boolean","description":"True when pricing a line so it sells for less than the item costs needs order.discount where the sale is made, whatever discount a role allows. The cost is what the goods would leave stock at there: what that location holds (its oldest layer under FIFO, else its average), else the cost the books assume for goods it does not hold (app.assumed_unit_cost: its last cost there, then other locations' stock, then the vendors'). False (the default) leaves only the discount rules."},"return_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after goods were handed over they are taken back on their order, with the receipt: 30 takes them back for 30 days. A category's own returnable_days overrides it for its items. A return past it needs order.cancel where the goods come back. NULL (the default) takes goods back at any time."},"receiptless_customer_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most one customer can be given back for goods without a receipt over receiptless_customer_days days, tax included, all their returns together. Set, every return without a receipt must name its customer. NULL (the default) sets no such limit; receiptless_return_limit still holds for each return."},"receiptless_customer_days":{"type":"integer","minimum":1,"maximum":730,"description":"The days, counting the day of the return, over which a customer's returns without a receipt are added up against receiptless_customer_limit. 30 by default."},"receiptless_cash_for_credit_purchases":{"type":"boolean","description":"Under receiptless_returns any_tender: true lets goods a named customer bought with store credit or a gift card come back without a receipt for money. False (the default) gives them back only as store credit: an item the customer bought on an order paid in part with store credit or a gift card, within receiptless_return_price_days (or ever, when that is NULL), comes back as store credit. Goods a lender paid for never come back without a receipt: they go back on their order, to the lender. Goods brought back by someone not named cannot be traced to a purchase."},"receiptless_traced_days":{"type":"integer","minimum":0,"maximum":730,"description":"How many days back a return without a receipt looks for sales of the item on orders a lender or a gift card paid for. Within them, the goods come back for money only when the customer named is the one who bought them that way (and then by the rules for that buyer); brought back by someone not named, or by another customer, they may be those goods, so they go back on the order they were sold on, or as store credit to a named customer. 90 by default; 0 turns the check off. Goods bought with a gift card are let through when receiptless_cash_for_credit_purchases is true; goods a lender paid for never are."},"package_discount_allocation":{"type":"string","enum":["proportional","main_component"],"description":"How a package's price is shared among its components, which decides each one's revenue, tax, commission and what a return of it gives back. proportional: in proportion to their regular prices, so each is discounted by the same share. main_component: the discount comes off the package's main component (the spa) first, as far as its own price goes, so accessories keep their regular prices; the rest, if any, in proportion. Applies when a package's price is next worked out."},"package_drop_credit":{"type":"string","enum":["none","share"],"description":"What the price of a package does when the customer leaves out a component the package lets go (package_component.can_drop). none: the package price holds, and the components kept share it. share: the package price comes down by what the component was priced at in the package."},"stock_aging_bucket_days":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"integer","minimum":1,"maximum":3650},"description":"The ages, in days on hand, where the stock aging read starts a new bucket, in increasing order: the default 90, 180, 270 gives 0-90, 91-180, 181-270 and over 270."},"receivable_aging_bucket_days":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"integer","minimum":1,"maximum":3650},"description":"The days overdue where the receivables aging starts a new bucket, in increasing order: the default 30, 60, 90 gives current (not yet past due), 1-30, 31-60, 61-90 and over 90 days overdue."},"site_readiness_checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The checklist a site check answers when none is named: what this business checks before a delivery (pad, electrical, access). NULL until it has one."},"delivery_checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"A checklist the crew answer at every delivery before it is signed off. NULL asks nothing."},"delivery_sign_off_required":{"type":"boolean","description":"True: a delivery is completed only with the customer's signature, or, by a key holding delivery.override, as left with nobody there, with photos. False: completeAppointment may complete a delivery with no sign-off."},"account_customers_pay_on_site":{"type":"boolean","description":"True: a customer with payment terms is asked to pay at the door like anyone, and the crew's day and the schedule show what to collect from them. False (the default): what such a customer owes goes on their account and statement, and the field shows it as on account rather than to collect."},"delivery_acceptance_statement":{"type":"string","minLength":1,"maxLength":2000,"description":"What the customer signs under at a delivery. Copied onto each sign-off as it stood then."},"dispatch_requires_ready_site":{"type":"boolean","description":"True: a crew cannot be marked on its way to a delivery whose site is not shown ready, except by a key holding delivery.override, giving a reason. False: readiness is shown, never enforced."},"site_ready_days_before":{"type":"integer","minimum":0,"maximum":60,"description":"How many days before a delivery its site must be shown ready. From then a delivery not ready is overdue, and the customer is reminded what is still needed."},"appointment_reminder_hours":{"anyOf":[{"type":"integer","minimum":1,"maximum":720},{"type":"null"}],"description":"How many hours before a visit the customer is reminded of it (the appointment.upcoming event). NULL sends no reminders."},"redelivery_fee_item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The service item charged when a delivery fails for a reason in redelivery_fee_reasons, at its price. NULL charges nothing. Accepts the record's id or its SKU.","x-natural-key":"sku"},"redelivery_fee_reasons":{"maxItems":10,"type":"array","items":{"type":"string","enum":["site_not_ready","access_blocked","customer_absent","customer_refused","unit_damaged","wrong_unit","outside_party_no_show","weather","vehicle_or_crew","other"]},"description":"The reasons a failed delivery is charged for: any of site_not_ready, access_blocked, customer_absent, customer_refused, unit_damaged, wrong_unit, outside_party_no_show, weather, vehicle_or_crew, other. Empty charges for none."},"redelivery_party_item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The service item a charged failure passes on what each outside party the business booked (a crane) and marked done bills for the visit as: one line of it per party, at their cost_amount, beside the redelivery fee and reported apart from it. NULL passes nothing on. Accepts the record's id or its SKU.","x-natural-key":"sku"},"service_labor_item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The item hourly labor on a job is charged as, unless the job names another: a service sold by the hour, in fractions. NULL: a job's customer time cannot be charged until one is set, and finishing a visit with some says so. Accepts the record's id or its SKU.","x-natural-key":"sku"},"travel_time_billing":{"type":"string","enum":["no_charge","customer"],"description":"Who pays for a technician's travel to a job, by default: no_charge (the default; most businesses charge a service-call fee instead, so travel is recorded for cost only) or customer (charged by the hour as labor)."},"labor_billing_increment_minutes":{"type":"integer","minimum":1,"maximum":60,"description":"Customer time on a visit is rounded up to this many minutes before it is charged. 1 (the default) charges the minutes worked."},"labor_billing_minimum_minutes":{"type":"integer","minimum":0,"maximum":480,"description":"The least time charged on a visit with any customer time. Time on a job but on no visit pays it only when none of the job's visits has customer time. 0 (the default) sets no minimum."},"service_sign_off_required":{"type":"boolean","description":"True (the default: the signature is the dealer's proof the customer accepted the work and its charges) when a service visit is completed only with the customer's signature on the completed work, or, by someone holding labor.manage, recorded as done with nobody there, with photos."},"service_authorization_statement":{"type":"string","minLength":1,"maxLength":2000,"description":"What a customer signs under when authorizing work on a job before it is done."},"service_completion_statement":{"type":"string","minLength":1,"maxLength":2000,"description":"What a customer signs under when accepting work on a job as done."},"default_labor_cost_rate":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What an hour of a technician's time costs the business when they have no cost rate of their own: wages plus burden. Reported only, never posted. NULL: their time has no cost in reports, and the reports say so."},"field_record_max_age_days":{"type":"integer","minimum":1,"maximum":90,"description":"How many days back a technician's device may date the time and parts it records on a job: what an offline device uploads late. Older needs labor.manage. 14 by default."},"account_payment_application":{"type":"string","enum":["oldest_first","none"],"description":"What happens to a payment on account (takeAccountPayment) when the request does not say. oldest_first (the default): it pays the orders the customer owes on, the one due first first, and whatever is left stays as account credit. none: it all stays as account credit until someone applies it (applyAccountCredit)."},"cash_needs_drawer":{"type":"boolean","description":"True: cash taken or given back (a payment, a refund, a return without a receipt) must name an open drawer at the location it happens, so every dollar is in some drawer's count. False (the default): naming a drawer is optional, and cash not in a drawer posts to the cash method's account as before. A payment that turned out not to be money (a reversal) never needs one."},"cash_count_blind":{"type":"boolean","description":"True (the default): whoever counts a drawer is not shown what it should hold, nor how far over or short the count was, nor which drawer took each payment. Only a key holding cash.manage or cost.view at the location sees those figures: cost.view reads the books, which show what each drawer holds, so the business decides who counts blind by who holds cost.view. False: everyone who can see a drawer sees them."},"cash_count_by_denomination":{"type":"boolean","description":"True: closing or recounting a drawer needs the count by denomination (twenty $20 bills, ...), which must add up to the figure counted. False (the default): the total alone will do, and a count by denomination is kept when one is sent."},"check_number_required":{"type":"boolean","description":"True (the default): money taken by check, on an order, on account or for a gift card, needs the check's number in reference, so the deposit slip and the bank statement can be matched, and so does money given back by a check the business writes: a refund on an order, account credit, a gift card cashed out. False: the number may be left out."},"cash_over_short_tolerance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How far over or short a drawer may be counted and still be reconciled when it is closed, with no manager looking at it. 0 (the default) sends every drawer that is off by a cent to a manager (reconcileCashDrawer, cash.manage)."},"cash_paid_out_limit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most cash that may be paid into or out of a drawer at once, for a reason other than a sale, without cash.manage at the location. 0 (the default) needs cash.manage for every paid-in and paid-out."},"cash_float_limit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The largest float a drawer may be opened with, out of the safe, without cash.manage at the location. 0 (the default) needs cash.manage for any float; a drawer opened empty needs none."},"gift_cards_offered":{"type":"boolean","description":"True when the business sells gift cards: selling, reloading and giving promotional cards, and refunding money an order took onto a card as new value. False by default, so no card is sold until the business decides to. Cards already sold can always be spent, replaced and written off, and the card value an order holds refunded onto a card (a new one too), whatever this says."},"gift_card_max_load":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most that can be put onto one gift card in one business day, sold, reloaded and given together. 2000.00 by default: the federal threshold under which a store's own cards are not treated as prepaid access a money services business must register for. Changing it is the business's call, and its accountant's."},"gift_card_refund_to_card":{"type":"boolean","description":"True (the default) when money an order was paid by gift card goes back onto a gift card before anything is refunded as money (or into account credit, which is money), so a card is never turned into cash by a return. False lets it go back by any method."},"gift_card_cash_out_below":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A card holding less than this can be cashed out on request (cashOutGiftCard), as some states require (California: under 10.00). NULL (the default) cashes out no card."},{"type":"null"}],"description":"A card holding less than this can be cashed out on request (cashOutGiftCard), as some states require (California: under 10.00). NULL (the default) cashes out no card."},"gift_card_breakage_after_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":36500},{"type":"null"}],"description":"How many days a card must go unused before what it holds may be written off as breakage, taken into income as never to be spent (writeOffGiftCard). NULL (the default) takes no breakage: the balance stays owed until it is spent or written off as unclaimed property. Whether and when breakage may be taken is a question for the business's accountant."},"gift_card_escheat_after_days":{"type":"integer","minimum":1,"maximum":36500,"description":"How many days a card must go unused before what it holds may be written off as unclaimed property owed to the state (writeOffGiftCard, escheat). 1825 (five years) by default, the longest dormancy period any state uses, so no card is reported early; set it to the business's state's period. Idaho exempts cards that neither expire nor charge fees, and these do neither, so a business there may never need it."},"payment_link_valid_days":{"type":"integer","minimum":1,"maximum":60,"description":"How many days a payment link sent to a customer works for (sendPaymentLink), counted from when it is sent. 7 by default. Once it has passed the customer is sent a new one."},"receipt_header":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"The business's own words printed at the top of every receipt, under its name and address, for example a slogan or \"Thank you for shopping local\". Up to 500 characters. NULL (the default): nothing is printed there."},{"type":"null"}],"description":"The business's own words printed at the top of every receipt, under its name and address, for example a slogan or \"Thank you for shopping local\". Up to 500 characters. NULL (the default): nothing is printed there."},"receipt_footer":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"The business's own words printed at the bottom of every receipt, for example its website or service hours. Up to 500 characters. NULL (the default): nothing is printed there."},{"type":"null"}],"description":"The business's own words printed at the bottom of every receipt, for example its website or service hours. Up to 500 characters. NULL (the default): nothing is printed there."},"receipt_return_policy":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"The business's return policy as it wants customers to read it, printed on every receipt and gift receipt, for example \"Unopened chemicals may be returned within 30 days with this receipt.\" Up to 1000 characters. NULL (the default): no return policy is printed."},{"type":"null"}],"description":"The business's return policy as it wants customers to read it, printed on every receipt and gift receipt, for example \"Unopened chemicals may be returned within 30 days with this receipt.\" Up to 1000 characters. NULL (the default): no return policy is printed."},"receipt_email_automatically":{"type":"boolean","description":"True: each receipt issued for a customer with an email address is emailed to them as it is issued, as a service message their consent allows. False (the default): a receipt is emailed only when someone asks (emailReceipt)."},"receipt_shows_regular_price":{"type":"boolean","description":"True (the default): a receipt line sold below what it lists at for the sale, at a price set by hand, prints that price as its regular price beneath it, so the customer sees what they saved. False: receipts print only the price agreed."},"fiscal_year_start_month":{"type":"integer","minimum":1,"maximum":12,"description":"The month the business's fiscal year starts, 1 for January to 12 for December. Reports count fiscal quarters and years from it, and the financial statement counts earnings for the year from it. 1 (the default): the fiscal year is the calendar year."},"week_start_day":{"type":"string","enum":["monday","sunday"],"description":"The day a week starts on in reports grouped by week: monday (the default, as ISO weeks) or sunday."},"staff_session_hours":{"type":"integer","minimum":1,"maximum":24,"description":"How long a person stays signed in, in hours from 1 to 24: a sign-in issues a session key that stops working this long after, on the business's clock, and they sign in again. 12 (the default) covers a working day. A change applies to sign-ins from then on."},"shared_device_idle_minutes":{"type":"integer","minimum":1,"maximum":120,"description":"On a computer a person marked as shared when signing in (a store counter), how many minutes from 1 to 120 without anyone using it before the person is signed out, so the next person cannot act as them. 5 (the default) suits a busy counter. The web app applies it; a session key itself still lasts staff_session_hours."},"pin_length":{"type":"integer","minimum":4,"maximum":8,"description":"How many digits a new PIN has, from 4 to 8: what a person sets to switch in on a counter device (setPin). 6 (the default) gives a million to guess from, and five wrong ones lock it. A PIN set before a change keeps working."},"device_session_minutes":{"type":"integer","minimum":5,"maximum":720,"description":"How long a person switched in on a counter device with their PIN stays signed in there, in minutes from 5 to 720: the session stops working this long after, on the business's clock, and never after the device itself. 30 (the default) covers a busy stretch at the counter."},"device_idle_minutes":{"type":"integer","minimum":1,"maximum":60,"description":"On a counter device, how many minutes from 1 to 60 without anyone using it before the person switched in is signed out and the PIN pad shows again. 5 (the default) suits a busy counter. The web app applies it."},"device_sign_out_after_sale":{"type":"boolean","description":"True (the default) when a person switched in on a counter device is signed out once a sale is done with, so the next person switches in as themselves and the sale is credited right. False keeps them signed in until they sign out or the device sits idle. The web app applies it."},"sales_desk_idle_minutes":{"type":"integer","minimum":1,"maximum":120,"description":"While a quote or order is open on screen, how many minutes from 1 to 120 without anyone using it before a person on a shared computer or counter device is signed out, when that is longer than the usual idle limit (shared_device_idle_minutes, device_idle_minutes). A customer reads a quote across the desk for minutes without a tap; 15 (the default) lets them, and anything else on screen keeps the usual limit. The web app applies it."},"lead_owner":{"type":"string","enum":["captor","unassigned"],"description":"Who owns a lead someone takes down without naming an owner: captor (the default), the person who took it down, as a showroom works; unassigned, every new lead waits for a manager (lead.manage) to hand it out. A lead from a form or another system, taken down by no one, always waits."},"lead_follow_up_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":60},{"type":"null"}],"description":"When the first follow-up on a new lead falls due, in days after it is taken down, unless the person taking it down sets one: 1 (the default) is the next day, 0 the same day. NULL: no follow-up is set unless one is given."},"lead_stale_days":{"type":"integer","minimum":1,"maximum":365,"description":"How many days an open lead may go with nothing logged before it shows as going cold. 14 by default."}},"additionalProperties":false,"examples":[{"allow_negative_stock":false},{"books_closed_through":"2026-09-30"},{"hold_when_overdue_days":30},{"finance_charges":true,"finance_charge_monthly_percent":"1.500","finance_charge_minimum":"2.00"},{"store_credit_offered":true,"receiptless_returns":"store_credit","receiptless_return_limit":"100.00"},{"cancellation_fee_percent":"10.00","restocking_fee_percent":"15.00","quote_valid_days":30},{"min_deposit_percent":"25.00"},{"stock_aging_bucket_days":[60,120,365]},{"receivable_aging_bucket_days":[15,45]},{"package_discount_allocation":"main_component","package_drop_credit":"share"},{"cash_needs_drawer":true,"cash_over_short_tolerance":"1.00","cash_paid_out_limit":"50.00","cash_float_limit":"300.00"},{"gift_cards_offered":true,"gift_card_cash_out_below":"10.00"},{"fiscal_year_start_month":7,"week_start_day":"sunday"},{"service_labor_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","labor_billing_increment_minutes":15,"labor_billing_minimum_minutes":60},{"receipt_return_policy":"Unopened chemicals may be returned within 30 days with this receipt."},{"redelivery_fee_item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","redelivery_fee_reasons":["site_not_ready","customer_absent"]}]},"CreateCustomFieldInput":{"type":"object","properties":{"table_name":{"type":"string","enum":["appointment","customer","equipment","item","purchase_order","sales_order","serialized_unit","site","subscription","vendor","work_order"],"description":"The table the field extends, for example item or customer. Only tables with a custom_fields column can be extended. Fixed once the field exists."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$","description":"The field's name inside custom_fields: lowercase snake_case, unique per table. Fixed once the field exists."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Label shown to people, for example \"Seats\"."},"description":{"type":"string","minLength":1,"maxLength":1000,"description":"What the field means and when to fill it in, written for someone (or a model) who has never seen the business."},"data_type":{"type":"string","enum":["text","number","boolean","date","choice"],"description":"text, number, boolean, date (a YYYY-MM-DD string) or choice (one of choices). Fixed once the field exists."},"choices":{"description":"The allowed values when data_type is choice. NULL otherwise.","minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":100}}},"required":["table_name","key","name","data_type"],"additionalProperties":false,"examples":[{"table_name":"item","key":"seats","name":"Seats","description":"How many people the hot tub seats.","data_type":"number"},{"table_name":"item","key":"voltage","name":"Voltage","data_type":"choice","choices":["120","240"]}]},"UpdateCustomFieldInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Label shown to people, for example \"Seats\"."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What the field means and when to fill it in, written for someone (or a model) who has never seen the business."},{"type":"null"}],"description":"What the field means and when to fill it in, written for someone (or a model) who has never seen the business."},"choices":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"The allowed values when data_type is choice. NULL otherwise."},"is_active":{"type":"boolean","description":"False once the field is retired: values already stored stay, but it can no longer be set."}},"additionalProperties":false,"examples":[{"choices":["120","240","240 (50A)"]},{"is_active":false}]},"SetCustomFieldValuesInput":{"type":"object","properties":{"table_name":{"type":"string","enum":["appointment","customer","equipment","item","purchase_order","sales_order","serialized_unit","site","subscription","vendor","work_order"],"description":"The table of the record."},"row_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The id of the record."},"values":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","maxLength":40,"description":"The field's key, from listCustomFields."},"value":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"The value, written as text whatever the field's type: 6, true, 2026-10-02, or one of the choices. Null clears it."}},"required":["key","value"],"additionalProperties":false},"description":"The fields to set. Fields not sent keep their values."}},"required":["table_name","row_id","values"],"additionalProperties":false,"examples":[{"table_name":"item","row_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","values":[{"key":"seats","value":"6"},{"key":"voltage","value":"240"}]}]},"CreateStaffMemberInput":{"type":"object","properties":{"given_name":{"type":"string","minLength":1,"maxLength":200,"description":"First name."},"family_name":{"type":"string","minLength":1,"maxLength":200,"description":"Last name."},"email":{"anyOf":[{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+$"},{"type":"null"}],"description":"Work email address, lowercase, unique within the tenant."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Work or mobile phone number in E.164 form, for example +12085550100."},"job_title":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Role as the business describes it, for example \"Service Technician\"."},{"type":"null"}],"description":"Role as the business describes it, for example \"Service Technician\"."},"home_location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The location this person normally works from. NULL for staff who are not tied to one. Accepts the record's id or its branch code.","x-natural-key":"branch_code"},"commission_plan_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The commission plan this person is paid on. NULL for staff who earn no commission."},"roles":{"default":[],"description":"Roles to give the new person straight away. Each is given as by grantStaffRole.","maxItems":20,"type":"array","items":{"type":"object","properties":{"role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The role held."},"location_id":{"default":null,"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The one location the role applies at. NULL when it applies everywhere. A role held at one location gives its permissions only there. Accepts the record's id or its branch code.","x-natural-key":"branch_code"}},"required":["role_id"],"additionalProperties":false}}},"required":["given_name","family_name"],"additionalProperties":false,"examples":[{"given_name":"Pat","family_name":"Lee","job_title":"Sales Associate"},{"given_name":"Dana","family_name":"Ortiz","email":"dana@thespateam.com","telephone":"+12085550123","job_title":"Service Technician","home_location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","roles":[{"role_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","location_id":null}]}]},"UpdateStaffMemberInput":{"type":"object","properties":{"given_name":{"type":"string","minLength":1,"maxLength":200,"description":"First name."},"family_name":{"type":"string","minLength":1,"maxLength":200,"description":"Last name."},"email":{"anyOf":[{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+$"},{"type":"null"}],"description":"Work email address, lowercase, unique within the tenant."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Work or mobile phone number in E.164 form, for example +12085550100."},"job_title":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Role as the business describes it, for example \"Service Technician\"."},{"type":"null"}],"description":"Role as the business describes it, for example \"Service Technician\"."},"home_location_id":{"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The location this person normally works from. NULL for staff who are not tied to one. Accepts the record's id or its branch code.","x-natural-key":"branch_code"},"commission_plan_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The commission plan this person is paid on. NULL for staff who earn no commission."},"is_active":{"type":"boolean","description":"False once the person has left. Former staff stay so past sales and work orders still name them."}},"additionalProperties":false,"examples":[{"job_title":"Service Manager"},{"is_active":false}]},"GrantStaffRoleInput":{"type":"object","properties":{"role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The role held."},"location_id":{"default":null,"anyOf":[{"type":"string","x-natural-key":"branch_code"},{"type":"null"}],"description":"The one location the role applies at. NULL when it applies everywhere. A role held at one location gives its permissions only there. Accepts the record's id or its branch code.","x-natural-key":"branch_code"}},"required":["role_id"],"additionalProperties":false,"examples":[{"role_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"},{"role_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"CreateRoleInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The role's name, unique within the tenant."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"Who the role is for."},{"type":"null"}],"description":"Who the role is for."},"permissions":{"default":[],"maxItems":50,"type":"array","items":{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"],"description":"An action the system can refuse. What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"description":"What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"max_discount_percent":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most its holders may take off a line without order.discount, as a percent of what the line comes to at its list price (what the store's and the customer's price lists give it, else the item's own price): a discount, a lower price, or both. A lower price a price list gives is not a discount. 10 lets a salesperson give up to 10% off on their own; more needs order.discount. NULL (the default) allows no discount without order.discount. A key holding several roles where the sale is made may give the largest of their allowances. Giving or changing an allowance needs order.discount for the whole business, as giving that permission does. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most its holders may take off a line without order.discount, as a percent of what the line comes to at its list price (what the store's and the customer's price lists give it, else the item's own price): a discount, a lower price, or both. A lower price a price list gives is not a discount. 10 lets a salesperson give up to 10% off on their own; more needs order.discount. NULL (the default) allows no discount without order.discount. A key holding several roles where the sale is made may give the largest of their allowances. Giving or changing an allowance needs order.discount for the whole business, as giving that permission does."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Sales Manager","description":"Runs a showroom's sales floor.","permissions":["order.discount","order.cancel","payment.refund","commission.view"]},{"name":"Salesperson","max_discount_percent":"10.00"}]},"UpdateRoleInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The role's name, unique within the tenant."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"Who the role is for."},{"type":"null"}],"description":"Who the role is for."},"permissions":{"maxItems":50,"type":"array","items":{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"],"description":"An action the system can refuse. What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"description":"What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"max_discount_percent":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most its holders may take off a line without order.discount, as a percent of what the line comes to at its list price (what the store's and the customer's price lists give it, else the item's own price): a discount, a lower price, or both. A lower price a price list gives is not a discount. 10 lets a salesperson give up to 10% off on their own; more needs order.discount. NULL (the default) allows no discount without order.discount. A key holding several roles where the sale is made may give the largest of their allowances. Giving or changing an allowance needs order.discount for the whole business, as giving that permission does. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most its holders may take off a line without order.discount, as a percent of what the line comes to at its list price (what the store's and the customer's price lists give it, else the item's own price): a discount, a lower price, or both. A lower price a price list gives is not a discount. 10 lets a salesperson give up to 10% off on their own; more needs order.discount. NULL (the default) allows no discount without order.discount. A key holding several roles where the sale is made may give the largest of their allowances. Giving or changing an allowance needs order.discount for the whole business, as giving that permission does."},"is_active":{"type":"boolean","description":"False once the role is retired. Its holders lose its permissions."}},"additionalProperties":false,"examples":[{"permissions":["order.discount","order.cancel"]},{"is_active":false}]},"GiveStoreCreditInput":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much credit to give. More than 0. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Why. Required for a goodwill credit: what it is for, for example \"scratched cover, kept at 40 off\"."},"staff_member_id":{"description":"Who gave the credit or took it in payment. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["amount","location_id","note","idempotency_key"],"additionalProperties":false,"examples":[{"amount":"40.00","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","note":"Cover arrived scratched; customer kept it at 40 off","idempotency_key":"0199a3c4-goodwill-1042"}]},"CreateSubscriptionInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the arrangement is called, for example \"Valet Care Diamond\" or \"Monthly chemicals\"."},"kind":{"type":"string","enum":["service_visit","shipment"],"description":"service_visit: each cycle is a visit by a technician. shipment: each cycle is goods sent or set aside for the customer."},"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"site_id":{"description":"Where the visit happens or the goods go: one of the customer's own sites.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"equipment_id":{"description":"The equipment a service visit looks after, which must be the customer's own.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"interval_unit":{"type":"string","enum":["day","week","month"],"description":"The unit of the rhythm: day, week or month. Fixed once a cycle has been generated."},"interval_count":{"type":"integer","minimum":1,"maximum":366,"description":"How many units between cycles: 2 with week is every two weeks. Fixed once a cycle has been generated."},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the arrangement, and the date the first cycle falls due. Every later cycle is counted from it. Fixed once a cycle has been generated."},"ends_on":{"description":"The last day of the current term. NULL for an arrangement with no end. Extending a term that has lapsed resumes it from today: the cycles missed meanwhile are skipped, not billed.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"auto_renew":{"default":false,"description":"True to extend the term by renewal_months when it runs out, instead of stopping.","type":"boolean"},"renewal_months":{"default":12,"description":"How many months each renewal adds to the term when auto_renew is on: 12 for a yearly plan, 3 for quarterly, 1 for monthly. A term that ends the day before a whole number of months from starts_on keeps doing so: renewing it never drifts toward the 28th.","type":"integer","minimum":1,"maximum":120},"lead_days":{"default":0,"description":"How many days before a cycle falls due its order may be made, so a visit can be scheduled or goods picked ahead of time. 0 makes the order on the due date.","type":"integer","minimum":0,"maximum":366},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff."},"lines":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many each cycle. 0 takes the item out of the subscription. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"description":"The price agreed for this subscription. NULL charges the item's price at the time of each cycle.","anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The price agreed for this subscription. NULL charges the item's price at the time of each cycle. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}]},"description":{"description":"What the order line should say. NULL uses the item's name.","anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"What the order line should say. NULL uses the item's name."},{"type":"null"}]}},"required":["item_id","quantity"],"additionalProperties":false},"description":"What each cycle includes."}},"required":["name","kind","customer_id","location_id","interval_unit","interval_count","starts_on","lines"],"additionalProperties":false,"examples":[{"name":"Valet Care Diamond","kind":"service_visit","customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","site_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","equipment_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7d","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7e","interval_unit":"week","interval_count":2,"starts_on":"2026-10-05","ends_on":"2027-10-04","auto_renew":true,"lead_days":7,"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7f","quantity":"1","unit_price":"89.00"}]}]},"UpdateSubscriptionInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the arrangement is called, for example \"Valet Care Diamond\" or \"Monthly chemicals\"."},"status":{"type":"string","enum":["active","paused","cancelled"],"description":"active: cycles are generated when due. paused: nothing is generated until it is made active again, and cycles missed meanwhile are skipped. cancelled: over, for good."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the visit happens or the goods go: one of the customer's own sites."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment a service visit looks after, which must be the customer's own."},"interval_unit":{"type":"string","enum":["day","week","month"],"description":"The unit of the rhythm: day, week or month. Fixed once a cycle has been generated."},"interval_count":{"type":"integer","minimum":1,"maximum":366,"description":"How many units between cycles: 2 with week is every two weeks. Fixed once a cycle has been generated."},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the arrangement, and the date the first cycle falls due. Every later cycle is counted from it. Fixed once a cycle has been generated."},"ends_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day of the current term. NULL for an arrangement with no end. Extending a term that has lapsed resumes it from today: the cycles missed meanwhile are skipped, not billed."},"auto_renew":{"type":"boolean","description":"True to extend the term by renewal_months when it runs out, instead of stopping."},"renewal_months":{"type":"integer","minimum":1,"maximum":120,"description":"How many months each renewal adds to the term when auto_renew is on: 12 for a yearly plan, 3 for quarterly, 1 for monthly. A term that ends the day before a whole number of months from starts_on keeps doing so: renewing it never drifts toward the 28th."},"lead_days":{"type":"integer","minimum":0,"maximum":366,"description":"How many days before a cycle falls due its order may be made, so a visit can be scheduled or goods picked ahead of time. 0 makes the order on the due date."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff."},{"type":"null"}],"description":"Free-form notes for staff."}},"additionalProperties":false,"examples":[{"status":"paused"},{"status":"active"},{"lead_days":10},{"auto_renew":true,"renewal_months":3}]},"SetSubscriptionLinesInput":{"type":"object","properties":{"lines":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many each cycle. 0 takes the item out of the subscription. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"description":"The price agreed for this subscription. NULL charges the item's price at the time of each cycle.","anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The price agreed for this subscription. NULL charges the item's price at the time of each cycle. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}]},"description":{"description":"What the order line should say. NULL uses the item's name.","anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"What the order line should say. NULL uses the item's name."},{"type":"null"}]}},"required":["item_id","quantity"],"additionalProperties":false},"description":"Items to set: each replaces the line for its item. Quantity 0 takes an item out. Items not sent are unchanged."}},"required":["lines"],"additionalProperties":false,"examples":[{"lines":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","quantity":"2"}]}]},"MakeSubscriptionOrderInput":{"type":"object","properties":{"cycle_due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The cycle to order: the subscription's next_due_on. Naming it makes a repeat safe: asked again for the same cycle, the order already made is returned."}},"required":["cycle_due_on"],"additionalProperties":false,"examples":[{"cycle_due_on":"2026-11-02"}]},"CreateTaxClassInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The class name, unique within the tenant."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What belongs in the class."},{"type":"null"}],"description":"What belongs in the class."},"is_default":{"default":false,"type":"boolean","description":"True for the one class used by items that name no class of their own."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Repair labor","description":"Labor on repairs, which Idaho does not tax."}]},"UpdateTaxClassInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The class name, unique within the tenant."},"description":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"What belongs in the class."},{"type":"null"}],"description":"What belongs in the class."},"is_default":{"type":"boolean","description":"True for the one class used by items that name no class of their own."},"is_active":{"type":"boolean","description":"False once the class is retired."}},"additionalProperties":false,"examples":[{"is_default":true},{"is_active":false}]},"CreateTaxJurisdictionInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The authority's name, unique within the tenant."},"trade_in_credit":{"default":"none","type":"string","enum":["none","same_class","all"],"description":"Whether this jurisdiction taxes a sale less what the customer was allowed for an old unit traded in toward it (trade_in, 0008): none, the whole price is taxed; same_class, the allowance comes off the taxed price of the order's lines in the same tax class as the item the old unit comes in as (give spas their own class for a like-kind rule); all, it comes off every taxed line. The allowance is shared over the lines in proportion to their price, never below nothing. A line taxed less this way is handed over only once the trade is received. Which applies is the business's to find out for each authority: none until it says otherwise."},"rates":{"default":[],"description":"Its rates, each as in setTaxRate.","maxItems":20,"type":"array","items":{"type":"object","properties":{"tax_class_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The class of item taxed."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction: 0.06 is six percent. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"effective_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first order date the rate applies to. The rate in force is the one with the latest effective_from on or before the order date. A date, YYYY-MM-DD."}},"required":["tax_class_id","rate","effective_from"],"additionalProperties":false}}},"required":["name"],"additionalProperties":false,"examples":[{"name":"State of Idaho","rates":[{"tax_class_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","rate":"0.06","effective_from":"2006-10-01"}]}]},"UpdateTaxJurisdictionInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The authority's name, unique within the tenant."},"is_active":{"type":"boolean","description":"False once the tenant no longer collects for it."},"trade_in_credit":{"type":"string","enum":["none","same_class","all"],"description":"Whether this jurisdiction taxes a sale less what the customer was allowed for an old unit traded in toward it (trade_in, 0008): none, the whole price is taxed; same_class, the allowance comes off the taxed price of the order's lines in the same tax class as the item the old unit comes in as (give spas their own class for a like-kind rule); all, it comes off every taxed line. The allowance is shared over the lines in proportion to their price, never below nothing. A line taxed less this way is handed over only once the trade is received. Which applies is the business's to find out for each authority: none until it says otherwise."}},"additionalProperties":false,"examples":[{"is_active":false}]},"SetTaxRateInput":{"type":"object","properties":{"tax_class_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The class of item taxed."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction: 0.06 is six percent. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"effective_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first order date the rate applies to. The rate in force is the one with the latest effective_from on or before the order date. A date, YYYY-MM-DD."}},"required":["tax_class_id","rate","effective_from"],"additionalProperties":false,"examples":[{"tax_class_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","rate":"0.06","effective_from":"2026-01-01"},{"tax_class_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","rate":"0","effective_from":"2026-07-01"}]},"CreateTaxAreaInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The area's name, unique within the tenant."},"tax_jurisdiction_ids":{"maxItems":20,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"description":"Every jurisdiction whose taxes apply in the area. Replaces the area's list."}},"required":["name","tax_jurisdiction_ids"],"additionalProperties":false,"examples":[{"name":"Hailey","tax_jurisdiction_ids":["0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c"]}]},"UpdateTaxAreaInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The area's name, unique within the tenant."},"is_active":{"type":"boolean","description":"False once the area is retired."},"tax_jurisdiction_ids":{"maxItems":20,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"description":"Every jurisdiction whose taxes apply in the area. Replaces the area's list."}},"additionalProperties":false,"examples":[{"tax_jurisdiction_ids":["0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"]},{"is_active":false}]},"CreateLeadSourceInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Z0-9][A-Z0-9_-]{1,31}$","description":"What staff and forms quote, uppercase, unique within the business: WALKIN, WEB, HOMESHOW-2026."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"The name people see, unique within the business: \"Boise Home & Garden Show 2026\"."},"kind":{"type":"string","enum":["walk_in","phone","web","show","referral","advertising","other"],"description":"What sort of source it is, so reports can add up across them: walk_in, phone, web, show (a home show or event), referral, advertising, other."}},"required":["code","name","kind"],"additionalProperties":false,"examples":[{"code":"HOMESHOW-26","name":"Boise Home & Garden Show 2026","kind":"show"}]},"UpdateLeadSourceInput":{"type":"object","properties":{"code":{"type":"string","pattern":"^[A-Z0-9][A-Z0-9_-]{1,31}$","description":"What staff and forms quote, uppercase, unique within the business: WALKIN, WEB, HOMESHOW-2026."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"The name people see, unique within the business: \"Boise Home & Garden Show 2026\"."},"kind":{"type":"string","enum":["walk_in","phone","web","show","referral","advertising","other"],"description":"What sort of source it is, so reports can add up across them: walk_in, phone, web, show (a home show or event), referral, advertising, other."},"is_active":{"type":"boolean","description":"False once it is no longer used (a show that is over). Its leads keep it."}},"additionalProperties":false,"examples":[{"is_active":false},{"name":"Boise Home Show, March 2026"}]},"CaptureLeadInput":{"type":"object","properties":{"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"given_name":{"type":"string","minLength":1,"maxLength":100,"description":"First name. Only for a person."},"family_name":{"type":"string","minLength":1,"maxLength":100,"description":"Last name, used for sorting and lookup. Only for a person."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The name of a business, or a person's name in one piece when it does not split."},"telephone":{"description":"Their phone number, as given: 208-555-0100 or +12085550100. Used to find them if they are already on file, and kept on file if they are not.","type":"string","minLength":7,"maxLength":30},"email":{"description":"Their email address. Used and kept as the phone number is.","type":"string","pattern":"^[^@\\s]+@[^@\\s]+$"},"marketing_consent":{"default":"unknown","description":"Whether they agreed, just now, to hear about offers at the phone and email given: granted, denied, or unknown (not asked). It never turns a refusal already on file into agreement (updateContactPoint does that).","type":"string","enum":["unknown","granted","denied"]},"consent_source":{"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\". Defaults to who took the lead down, where and when, or to the source for a form.","type":"string","minLength":1,"maxLength":500},"lead_source_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its lead source code."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"The salesperson who owns it and owes its follow-ups. NULL while no one does, waiting for a manager to hand it out. Who it starts with is the business's choice (tenant_policy.lead_owner). Giving it to someone else needs lead.manage at its store. Leave it out for the business's choice (getPolicy, lead_owner). Naming someone other than yourself needs lead.manage at the store.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}]},"interest":{"type":"string","minLength":1,"maxLength":2000,"description":"What they are after, in their words: \"6-person spa for the back deck, wants lounger, asking about 0% financing\"."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"follow_up":{"description":"Its first follow-up. Leave it out for the business's (getPolicy, lead_follow_up_days), or send null for none.","anyOf":[{"type":"object","properties":{"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","minLength":1,"maxLength":2000,"description":"What to do: \"Call about the 0% offer\"."}},"required":["due_on","note"],"additionalProperties":false,"description":"A follow-up to add with it: when it is due and what to do."},{"type":"null"}]},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["lead_source_id","interest"],"additionalProperties":false,"examples":[{"given_name":"Jordan","family_name":"Reyes","telephone":"208-555-0142","email":"jordan.reyes@example.com","marketing_consent":"granted","lead_source_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a70","interest":"6-person spa for the back deck, wants a lounger. Asked about 0% financing.","follow_up":{"due_on":"2026-10-09","note":"Call about the 0% offer"},"idempotency_key":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a71","lead_source_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a72","interest":"Swim spa for the new pool house"}]},"UpdateLeadInput":{"type":"object","properties":{"interest":{"type":"string","minLength":1,"maxLength":2000,"description":"What they are after, in their words: \"6-person spa for the back deck, wants lounger, asking about 0% financing\"."},"item_id":{"anyOf":[{"type":"string","x-natural-key":"sku"},{"type":"null"}],"description":"The model they are most interested in, when it is one the business sells. Accepts the record's id or its SKU.","x-natural-key":"sku"},"lead_source_id":{"type":"string","x-natural-key":"code","description":"Accepts the record's id or its lead source code."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The salesperson who owns it and owes its follow-ups. NULL while no one does, waiting for a manager to hand it out. Who it starts with is the business's choice (tenant_policy.lead_owner). Giving it to someone else needs lead.manage at its store. Someone who works from its store may take a lead no one owns; giving it to someone else, or taking it from them, needs lead.manage at its store."},"stage":{"type":"string","enum":["new","contacted","visited","quoted"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages. Sent, it moves an open lead to another open stage by hand. Lose it with markLeadLost; it is won by its order."}},"additionalProperties":false,"examples":[{"staff_member_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"},{"stage":"visited"}]},"MarkLeadLostInput":{"type":"object","properties":{"lost_reason":{"type":"string","enum":["price","bought_elsewhere","financing","not_ready","no_response","other"],"description":"Why it was lost: price, bought_elsewhere (a competitor sold them one), financing (could not get it), not_ready (not this year), no_response (stopped answering), other (say what in lost_note). Set only for a lost lead."},"lost_note":{"type":"string","minLength":1,"maxLength":2000,"description":"More on why it was lost: who they bought from, what price beat ours."}},"required":["lost_reason"],"additionalProperties":false,"examples":[{"lost_reason":"price","lost_note":"Bought a Costco spa for $4,000 less"}]},"LogLeadActivityInput":{"type":"object","properties":{"kind":{"type":"string","enum":["call","text","email","visit","wet_test","note","inquiry"],"description":"What happened. Logged by people: call, text, email (each either way), visit (they came in, or were visited at home), wet_test (they soaked in a spa), note, inquiry (they asked again). Written by the system: captured, assigned, stage (moved by hand), quote (a quote was linked), won, lost. A call, text or email moves a new lead to contacted; a visit or wet test moves it to visited."},"summary":{"type":"string","minLength":1,"maxLength":4000,"description":"What happened, in a sentence or two: \"Soaked in the Grandee and the Vanguard; liked the lounger. Wife wants to see the cover colors.\""},"occurred_at":{"description":"When it happened: now unless said otherwise, never in the future.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"follow_up":{"type":"object","properties":{"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","minLength":1,"maxLength":2000,"description":"What to do: \"Call about the 0% offer\"."}},"required":["due_on","note"],"additionalProperties":false,"description":"A follow-up to add with it: when it is due and what to do."},"idempotency_key":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,200}$","description":"A key you make up for this request, 8 to 200 letters, digits, dots, colons, underscores or dashes, for example a fresh UUID, and send again unchanged if you retry it. A repeat with the same key does nothing and answers with what the first one did, so a retry can never take money or move stock twice. Use a new key for each new request."}},"required":["kind","summary"],"additionalProperties":false,"examples":[{"kind":"wet_test","summary":"Soaked in the Grandee and the Vanguard; liked the lounger. Wife wants to see the cover colors.","follow_up":{"due_on":"2026-10-12","note":"Send the cover colors"}},{"kind":"call","summary":"Left a message about the 0% offer"}]},"AddLeadTaskInput":{"type":"object","properties":{"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","minLength":1,"maxLength":2000,"description":"What to do: \"Call about the 0% offer\"."}},"required":["due_on","note"],"additionalProperties":false,"description":"A follow-up to add with it: when it is due and what to do.","examples":[{"due_on":"2026-10-12","note":"Call about delivery to the cabin"}]},"CompleteLeadTaskInput":{"type":"object","properties":{"activity":{"description":"What came of it, logged on the lead as it is marked done.","type":"object","properties":{"kind":{"type":"string","enum":["call","text","email","visit","wet_test","note","inquiry"],"description":"What happened. Logged by people: call, text, email (each either way), visit (they came in, or were visited at home), wet_test (they soaked in a spa), note, inquiry (they asked again). Written by the system: captured, assigned, stage (moved by hand), quote (a quote was linked), won, lost. A call, text or email moves a new lead to contacted; a visit or wet test moves it to visited."},"summary":{"type":"string","minLength":1,"maxLength":4000,"description":"What happened, in a sentence or two: \"Soaked in the Grandee and the Vanguard; liked the lounger. Wife wants to see the cover colors.\""}},"required":["kind","summary"],"additionalProperties":false},"follow_up":{"description":"The next follow-up, if one is owed.","type":"object","properties":{"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","minLength":1,"maxLength":2000,"description":"What to do: \"Call about the 0% offer\"."}},"required":["due_on","note"],"additionalProperties":false}},"additionalProperties":false,"examples":[{"activity":{"kind":"call","summary":"Reached him: coming in Saturday for a wet test"},"follow_up":{"due_on":"2026-10-11","note":"Wet test at 10, Grandee filled"}},{}]},"UpdateLeadTaskInput":{"type":"object","properties":{"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","minLength":1,"maxLength":2000,"description":"What to do: \"Call about the 0% offer\"."}},"additionalProperties":false,"examples":[{"due_on":"2026-10-15"}]},"SetLayawayScheduleInput":{"type":"object","properties":{"installments":{"minItems":1,"maxItems":104,"type":"array","items":{"type":"object","properties":{"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is to be paid by, at the order's store. Each comes after the one before."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much is due by due_on, before what has been paid. The active installments add up to the order's total when the schedule is set; should the order's total change, the last one takes the difference. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["due_on","amount"],"additionalProperties":false},"description":"Each payment, in date order, the first usually today's down payment. They add up to the order's total. Money already paid counts against them in date order."}},"required":["installments"],"additionalProperties":false,"examples":[{"installments":[{"due_on":"2026-10-07","amount":"200.00"},{"due_on":"2026-10-21","amount":"200.00"},{"due_on":"2026-11-04","amount":"248.37"}]}]},"CreateWaterBodyInput":{"type":"object","properties":{"customer_id":{"type":"string","x-natural-key":"customer_number","description":"Accepts the record's id or its customer number (as a number or a string of digits)."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is: one of the customer's own sites."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment that holds it, when that is on record. It must be the customer's own."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What to call it, for example \"Grandee hot tub\" or \"Back yard pool\"."},"kind":{"type":"string","enum":["hot_tub","swim_spa","pool","other"],"description":"hot_tub, swim_spa, pool or other. Target ranges can differ by kind."},"volume_gallons":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"How much water it holds, in US gallons. Doses scale with it; without it no dose can be worked out."},"sanitizer":{"anyOf":[{"type":"string","enum":["chlorine","bromine","salt","mineral","biguanide","other"]},{"type":"null"}],"description":"How the water is sanitized: chlorine, bromine, salt (a salt system making chlorine), mineral, biguanide or other."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff, for example a recurring problem."},{"type":"null"}],"description":"Free-form notes for staff, for example a recurring problem."}},"required":["customer_id","name","kind"],"additionalProperties":false,"examples":[{"customer_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","name":"Grandee hot tub","kind":"hot_tub","volume_gallons":"450","sanitizer":"chlorine"}]},"UpdateWaterBodyInput":{"type":"object","properties":{"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is: one of the customer's own sites."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment that holds it, when that is on record. It must be the customer's own."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What to call it, for example \"Grandee hot tub\" or \"Back yard pool\"."},"kind":{"type":"string","enum":["hot_tub","swim_spa","pool","other"],"description":"hot_tub, swim_spa, pool or other. Target ranges can differ by kind."},"volume_gallons":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"How much water it holds, in US gallons. Doses scale with it; without it no dose can be worked out."},"sanitizer":{"anyOf":[{"type":"string","enum":["chlorine","bromine","salt","mineral","biguanide","other"]},{"type":"null"}],"description":"How the water is sanitized: chlorine, bromine, salt (a salt system making chlorine), mineral, biguanide or other."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff, for example a recurring problem."},{"type":"null"}],"description":"Free-form notes for staff, for example a recurring problem."},"is_active":{"type":"boolean","description":"False once the customer no longer has it. Its test history stays."}},"additionalProperties":false,"examples":[{"volume_gallons":"475"},{"is_active":false}]},"RecordWaterTestInput":{"type":"object","properties":{"tested_at":{"description":"When the sample was tested: now unless given, and never more than a day ahead. Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"staff_member_id":{"description":"Who ran the test. Defaults to the staff member this API key acts for.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"source":{"default":"manual","description":"manual: readings typed in from strips or a kit. device: readings sent by a testing device or its software.","type":"string","enum":["manual","device"]},"source_reference":{"type":"string","minLength":1,"maxLength":200,"description":"The device's or outside system's own id for the test, when source is device."},"total_alkalinity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Total alkalinity, in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"ph":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"pH, from 0 to 14. A decimal string with up to 2 decimal places, for example \"12.50\"."},"calcium_hardness":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Calcium hardness, in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"cyanuric_acid":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Cyanuric acid (stabilizer), in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"free_chlorine":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Free chlorine, in ppm. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total_chlorine":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Total chlorine, in ppm. Total less free is combined chlorine, so it cannot be less than free_chlorine. A decimal string with up to 2 decimal places, for example \"12.50\"."},"bromine":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Bromine, in ppm. A decimal string with up to 2 decimal places, for example \"12.50\"."},"salt":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Salt, in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"notes":{"type":"string","minLength":1,"maxLength":5000,"description":"What was seen or said: cloudy water, a new fill, a customer complaint."}},"additionalProperties":false,"examples":[{"total_alkalinity":"60","ph":"7.0","free_chlorine":"0.5","calcium_hardness":"150","notes":"Cloudy after a party."}]},"SetTargetRangeInput":{"type":"object","properties":{"parameter":{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","bromine","salt"],"description":"The reading the range is for: one of the reading columns of water_test."},"water_body_kind":{"default":null,"description":"The kind of water body the range is for. NULL applies to every kind that has no range of its own. Setting a range again for the same reading and kind replaces it.","anyOf":[{"type":"string","enum":["hot_tub","swim_spa","pool","other"]},{"type":"null"}]},"minimum":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The lowest acceptable reading. A decimal string with up to 2 decimal places, for example \"12.50\"."},"maximum":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The highest acceptable reading. Treatments aim for the middle of the range. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["parameter","minimum","maximum"],"additionalProperties":false,"examples":[{"parameter":"free_chlorine","water_body_kind":"hot_tub","minimum":"3","maximum":"5"}]},"CreateDosingRuleInput":{"type":"object","properties":{"parameter":{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","bromine","salt"],"description":"The reading the product moves."},"direction":{"type":"string","enum":["raise","lower"],"description":"raise or lower."},"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The amount of product the label states. A decimal string with up to 3 decimal places, for example \"12.500\"."},"amount_unit":{"type":"string","minLength":1,"maxLength":20,"description":"The unit of amount, as the label gives it: oz, lb, tbsp, g."},"per_volume_gallons":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"The volume of water that amount is for, in US gallons. A decimal string with up to 1 decimal places, for example \"12.5\"."},"changes_by":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much that amount, in that volume, moves the reading. A decimal string with up to 2 decimal places, for example \"12.50\"."},"instructions":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"How to add it, copied onto each treatment: \"Dissolve first. Run jets 20 minutes, retest.\""},{"type":"null"}],"description":"How to add it, copied onto each treatment: \"Dissolve first. Run jets 20 minutes, retest.\""},"is_active":{"default":true,"type":"boolean","description":"False once the rule is retired, for example when the product is discontinued."},"packs":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"contents":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much product one of the item holds, in the rule's amount_unit: 80 for a 5 lb tub when the rule doses in oz. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","contents"],"additionalProperties":false},"description":"One size a dosing rule's product is sold in, and how much of the dose it holds: a 1 lb tub holds 16 oz. A treatment sells the smallest active pack that covers its dose, or as many of the largest as it takes. A rule with no packs sells its own item, one of it. schema.org: Offer. The sizes sent are the rule's packs: a size left out is no longer offered for it."}},"required":["parameter","direction","item_id","amount","amount_unit","per_volume_gallons","changes_by"],"additionalProperties":false,"examples":[{"parameter":"ph","direction":"raise","item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","amount":"1","amount_unit":"oz","per_volume_gallons":"500","changes_by":"0.2","instructions":"Dissolve first. Run jets 20 minutes, retest.","packs":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","contents":"32"},{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7c","contents":"160"}]}]},"UpdateDosingRuleInput":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The amount of product the label states. A decimal string with up to 3 decimal places, for example \"12.500\"."},"amount_unit":{"type":"string","minLength":1,"maxLength":20,"description":"The unit of amount, as the label gives it: oz, lb, tbsp, g."},"per_volume_gallons":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"The volume of water that amount is for, in US gallons. A decimal string with up to 1 decimal places, for example \"12.5\"."},"changes_by":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much that amount, in that volume, moves the reading. A decimal string with up to 2 decimal places, for example \"12.50\"."},"instructions":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"description":"How to add it, copied onto each treatment: \"Dissolve first. Run jets 20 minutes, retest.\""},{"type":"null"}],"description":"How to add it, copied onto each treatment: \"Dissolve first. Run jets 20 minutes, retest.\""},"is_active":{"type":"boolean","description":"False once the rule is retired, for example when the product is discontinued."},"packs":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","x-natural-key":"sku","description":"Accepts the record's id or its SKU."},"contents":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much product one of the item holds, in the rule's amount_unit: 80 for a 5 lb tub when the rule doses in oz. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","contents"],"additionalProperties":false},"description":"One size a dosing rule's product is sold in, and how much of the dose it holds: a 1 lb tub holds 16 oz. A treatment sells the smallest active pack that covers its dose, or as many of the largest as it takes. A rule with no packs sells its own item, one of it. schema.org: Offer. The sizes sent are the rule's packs: a size left out is no longer offered for it."}},"additionalProperties":false,"examples":[{"changes_by":"0.25"},{"is_active":false},{"packs":[{"item_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","contents":"80"}]}]},"ImportWaterLabFileInput":{"type":"object","properties":{"source":{"default":"generic_csv","description":"The lab's export format. generic_csv: A spreadsheet (CSV) export, one row per sample, the first row naming the columns. Columns are found by name in any order: Sample ID, Date, Time, Customer ID (the lab's id for the customer), Customer, Phone, Email, Water (hot tub, pool...), and the tests: pH, Total Alkalinity, Calcium Hardness, CYA, Free Chlorine, Total Chlorine, Bromine, Salt, each with its unit in brackets, e.g. Salt (g/L).","type":"string","enum":["generic_csv"]},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"file_name":{"type":"string","minLength":1,"maxLength":200,"description":"The name of the file uploaded, when there was one."},"content":{"type":"string","minLength":1,"maxLength":2000000,"description":"The file's text, exactly as the lab exported it."}},"required":["location_id","content"],"additionalProperties":false,"examples":[{"location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","file_name":"lab-2026-10-07.csv","content":"Sample ID,Date,Customer,Phone,Water,pH,Total Alkalinity (ppm),Free Chlorine (ppm)\n1042,2026-10-07 14:05,Avery Stone,208-555-0142,Hot tub,7.0,60,0.5\n"}]},"ImportWaterLabResultsInput":{"type":"object","properties":{"source":{"type":"string","pattern":"^[a-z][a-z0-9_]{1,39}$","description":"The adapter that read it, as a short lowercase key: generic_csv for a lab's spreadsheet export, or the integration's own key for results posted already read. The same key for every batch from one lab, so its sample and customer ids are recognized again."},"location_id":{"type":"string","x-natural-key":"branch_code","description":"Accepts the record's id or its branch code."},"samples":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"sample_reference":{"type":"string","minLength":1,"maxLength":100,"description":"The lab's own id for the test. Sent again, the sample is not taken up twice."},"tested_at":{"description":"When the lab tested it: when it came in, when the lab does not say. With its offset.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"lab_customer_reference":{"type":"string","minLength":1,"maxLength":100,"description":"The lab software's own id for the customer. Remembered when staff match the sample, so the next sample under it matches by itself."},"lab_customer_name":{"type":"string","minLength":1,"maxLength":200,"description":"The customer's name as the lab has it. Shown to staff; never matched on, as names are not unique."},"lab_telephone":{"type":"string","minLength":1,"maxLength":40,"description":"The customer's phone number as the lab has it. A ten-digit number is taken as North American."},"lab_email":{"description":"The customer's email address as the lab has it, in lowercase. Matched to a customer's address exactly.","type":"string","pattern":"^[^@\\s]+@[^@\\s]+$"},"lab_water_label":{"type":"string","minLength":1,"maxLength":200,"description":"What the lab calls the water: \"Hot tub\", \"Back yard pool\". Picks between a customer's waters when it is one's name."},"lab_water_kind":{"description":"The kind of water the lab says it is: hot_tub, swim_spa, pool or other. A customer's water of another kind is never matched to it.","type":"string","enum":["hot_tub","swim_spa","pool","other"]},"total_alkalinity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Total alkalinity, in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"ph":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"pH, from 0 to 14. A decimal string with up to 2 decimal places, for example \"12.50\"."},"calcium_hardness":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Calcium hardness, in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"cyanuric_acid":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Cyanuric acid (stabilizer), in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"free_chlorine":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Free chlorine, in ppm. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total_chlorine":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Total chlorine, in ppm. Total less free is combined chlorine, so it cannot be less than free_chlorine. A decimal string with up to 2 decimal places, for example \"12.50\"."},"bromine":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Bromine, in ppm. A decimal string with up to 2 decimal places, for example \"12.50\"."},"salt":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"Salt, in ppm. A decimal string with up to 1 decimal places, for example \"12.5\"."},"lab_readings":{"description":"The readings as the lab printed them, for the record. The readings above are what is used.","maxItems":60,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The lab's name for the test."},"value":{"type":"string","minLength":1,"maxLength":100,"description":"The value as the lab printed it."},"unit":{"default":null,"description":"The lab's unit, or null.","anyOf":[{"type":"string","minLength":1,"maxLength":40,"description":"The lab's unit."},{"type":"null"}]},"reading":{"default":null,"anyOf":[{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","total_chlorine","bromine","salt"]},{"type":"null"}],"description":"The reading of ours it became, or null when it was not used."},"note":{"default":null,"description":"Why it was not used, or how it was converted.","anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Why it was not used, or how it was converted."},{"type":"null"}]}},"required":["name","value"],"additionalProperties":false}}},"additionalProperties":false},"description":"The samples, each with its readings already in our units (ppm; pH as is)."}},"required":["source","location_id","samples"],"additionalProperties":false,"examples":[{"source":"waterlink_bridge","location_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b","samples":[{"sample_reference":"1042","tested_at":"2026-10-07T14:05:00-06:00","lab_customer_reference":"77","lab_telephone":"208-555-0142","lab_water_kind":"hot_tub","ph":"7.0","total_alkalinity":"60","free_chlorine":"0.5"}]}]},"MatchLabSampleInput":{"type":"object","properties":{"water_body_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer's water the sample is from. The lab's id for the customer is remembered, so their next sample matches by itself."}},"required":["water_body_id"],"additionalProperties":false,"examples":[{"water_body_id":"0199a3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"}]},"DiscardLabSampleInput":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why it was thrown out."}},"required":["reason"],"additionalProperties":false,"examples":[{"reason":"The lab's own check sample."}]},"CreateVendorInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The vendor's name, unique within the tenant."},"account_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The tenant's account number with this vendor, quoted on orders."},{"type":"null"}],"description":"The tenant's account number with this vendor, quoted on orders."},"email":{"anyOf":[{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+$"},{"type":"null"}],"description":"Where orders and enquiries are sent, lowercase."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Main phone number in E.164 form, for example +12085550100."},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"Website or ordering portal."},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Street address. A second line (suite, unit) follows a line break."},{"type":"null"}],"description":"Street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"City or town."},{"type":"null"}],"description":"City or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"State or province code."},{"type":"null"}],"description":"State or province code."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":20,"description":"ZIP or postal code."},{"type":"null"}],"description":"ZIP or postal code."},"address_country":{"default":"US","type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff: rep name, ordering quirks, freight terms."},{"type":"null"}],"description":"Free-form notes for staff: rep name, ordering quirks, freight terms."}},"required":["name"],"additionalProperties":false,"examples":[{"name":"Hot Spring Spas","account_number":"SRPS-0042","email":"orders@hotspring.example"},{"name":"BioGuard","url":"https://www.bioguard.example","notes":"Rep is Jamie. Freight free over 1,500."}]},"UpdateVendorInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The vendor's name, unique within the tenant."},"account_number":{"anyOf":[{"type":"string","minLength":1,"maxLength":100,"description":"The tenant's account number with this vendor, quoted on orders."},{"type":"null"}],"description":"The tenant's account number with this vendor, quoted on orders."},"email":{"anyOf":[{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+$"},{"type":"null"}],"description":"Where orders and enquiries are sent, lowercase."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Main phone number in E.164 form, for example +12085550100."},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"Website or ordering portal."},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Street address. A second line (suite, unit) follows a line break."},{"type":"null"}],"description":"Street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"City or town."},{"type":"null"}],"description":"City or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"State or province code."},{"type":"null"}],"description":"State or province code."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":20,"description":"ZIP or postal code."},{"type":"null"}],"description":"ZIP or postal code."},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"notes":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Free-form notes for staff: rep name, ordering quirks, freight terms."},{"type":"null"}],"description":"Free-form notes for staff: rep name, ordering quirks, freight terms."},"is_active":{"type":"boolean","description":"False once the tenant stops buying from this vendor. Its history stays."}},"additionalProperties":false,"examples":[{"telephone":"+18005550100"},{"is_active":false}]},"RepriceSalesOrderInput":{"type":"object","properties":{"include_hand_priced":{"default":false,"description":"Also put lines priced by hand at their list price. Needs order.discount at the order's location.","type":"boolean"}},"additionalProperties":false,"examples":[{},{"include_hand_priced":true}]},"NextAction":{"type":"object","properties":{"operation_id":{"type":"string","description":"The operationId of the operation to call, as in the OpenAPI document."},"method":{"type":"string","description":"Its HTTP method."},"path":{"type":"string","description":"Its path, with {placeholders} for path parameters."},"why":{"type":"string","description":"What calling it achieves from here."}},"required":["operation_id","method","path","why"],"additionalProperties":false,"description":"An operation worth calling next, and why. Offered on errors and on records whose state allows it."},"Problem":{"type":"object","properties":{"status":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"HTTP status code, repeated for clients that only see the body."},"code":{"type":"string","enum":["bad_request","validation_failed","unauthenticated","forbidden","not_found","conflict","invalid_reference","rule_violated","too_many_attempts","provider_unavailable","busy","internal_error"],"description":"Stable machine-readable error code. Branch on this, not on title or detail. bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again. unauthenticated: No valid API key was sent. Nothing you can change in the request body will help. forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for. not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones. conflict: A record with the same unique value already exists. Use the existing record or choose another value. invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first. too_many_attempts: Too many refused sign-ins from this client, or for this person, in a short time. Do not retry now: `detail` says when to try again. provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds. busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds. internal_error: The server failed. The request may or may not have been applied: read the record before sending it again, and quote request_id when reporting it."},"title":{"type":"string","description":"Short summary of the error class. The same for every error with this code."},"detail":{"type":"string","description":"What went wrong in this request and how to correct it."},"errors":{"description":"One entry per offending field. Present when the error can be pinned to fields.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Location of the offending value, for example body/time_zone."},"message":{"type":"string","description":"Why the value was rejected and what is accepted."}},"required":["path","message"],"additionalProperties":false}},"retryable":{"type":"boolean","description":"True when sending the same request again, unchanged, can succeed. False means change something first."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"request_id":{"type":"string","description":"Identifier of this request in the server logs and in the audit trail of anything it changed."}},"required":["status","code","title","detail","retryable","next","request_id"],"additionalProperties":false,"description":"Error response (RFC 9457 problem details)."},"MessageTemplate":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The template's name, unique within the tenant."},"channel":{"type":"string","enum":["email","sms"],"description":"email or sms."},"purpose":{"type":"string","enum":["service","marketing"],"description":"service: about the customer's own orders, deliveries and service. marketing: promotional. It decides which consent applies."},"subject":{"description":"The subject line, for email only.","type":["string","null"]},"body":{"type":"string","description":"The message text. Placeholders are written {{thing.field}} and filled in when the message is queued."},"is_active":{"type":"boolean","description":"False once the template is retired."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","channel","purpose","subject","body","is_active","created_at","updated_at"],"additionalProperties":false,"description":"The words of a message the tenant sends to customers, with places for the details: \"Hi {{customer.given_name}}, your delivery is booked for {{appointment.scheduled_window}}.\""},"MessageTemplatePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MessageTemplate"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of message templates."},"Automation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The automation's name, unique within the tenant."},"event_type":{"type":"string","enum":["customer.created","order.opened","order.completed","order.cancelled","sale.fulfilled","sale.returned","payment.received","payment.refunded","payment.reversed","appointment.scheduled","appointment.rescheduled","appointment.completed","appointment.cancelled","work_order.opened","work_order.completed","water_test.recorded","appointment.en_route","appointment.arrived","appointment.failed","appointment.upcoming","delivery.site_not_ready","site_check.recorded","account_payment.received","account_payment.refunded","account_payment.reversed","receipt.issued","statement.issued","work_order.signed_off","lead.created","lead.won","lead.lost"],"description":"The event that sets it off: one of the types listed on event.event_type that is about a customer, so any but stock.received."},"message_template_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The message to send."},"delay_minutes":{"type":"integer","minimum":0,"maximum":525600,"description":"How long after the event to send it. 0 sends at once."},"is_active":{"type":"boolean","description":"False while the automation is switched off."},"appointment_kind":{"anyOf":[{"type":"string","enum":["delivery","service","consultation"]},{"type":"null"}],"description":"For an appointment event: answer only visits of this kind (a delivery confirmation must not go to a service call). NULL: every kind."},"template_name":{"type":"string","description":"The template's name, unique within the tenant."},"placeholders":{"type":"array","items":{"type":"string"},"description":"The placeholders its template may use, as {{thing.field}}: the customer's and the business's, and those its event offers."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","event_type","message_template_id","delay_minutes","is_active","appointment_kind","template_name","placeholders","created_at","updated_at"],"additionalProperties":false,"description":"A standing instruction: when this kind of event happens, send the customer that message, after this long. \"When a delivery is scheduled, text the confirmation.\" \"Thirty days after a sale is fulfilled, email the care reminder.\""},"AutomationPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Automation"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of automations."},"AutomationRun":{"type":"object","properties":{"events_read":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many new events were looked at."},"messages_queued":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Messages queued to go out."},"messages_suppressed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Messages recorded but held back, because consent does not allow them."},"no_address":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Messages not written because the customer has no address for the channel."},"stale_skipped":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Events not answered because the news is out of date: a visit already over, or a crew on its way more than 6 hours ago."},"reminders_written":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Reminders that fell due and were written to the feed by this run (appointment.upcoming, delivery.site_not_ready). Their messages are queued by the next run."}},"required":["events_read","messages_queued","messages_suppressed","no_address","stale_skipped","reminders_written"],"additionalProperties":false,"description":"What one run of the automations did."},"Message":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer. Taken from the contact point."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"contact_point_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The address or number it goes to. Its kind must match the channel, and its consent decides whether it goes at all."},"to":{"type":"string","description":"The address or number it goes to."},"channel":{"type":"string","enum":["email","sms"],"description":"email or sms."},"purpose":{"type":"string","enum":["service","marketing"],"description":"service or marketing. A marketing message goes only where marketing consent is granted. A service message goes unless service consent is denied. A message written from a template has the template's purpose and channel. Fixed once the message is written."},"status":{"type":"string","enum":["queued","sending","sent","failed","suppressed"],"description":"queued: waiting for send_after. sending: handed to the email or text service, its answer not yet recorded; set and cleared only by the system. sent: accepted by the provider. failed: the provider refused it, or never confirmed it; status_reason says why. suppressed: not sent, because consent does not allow it, the address is dead, or it was withdrawn (held back by hand, or its visit moved or was called off while it waited); set by the system when the message is queued or taken to send, and by an outside sender if marking it sent is refused because consent was withdrawn meanwhile. Sent, failed and suppressed are final."},"status_reason":{"description":"Why the message failed or was suppressed.","type":["string","null"]},"message_template_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The template it was written from, if any."},"template_name":{"description":"The template's name, or null when the message was not made from one.","type":["string","null"]},"automation_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The automation that queued it, if any."},"automation_name":{"description":"The automation's name, or null when a person sent the message.","type":["string","null"]},"event_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The event that caused it, if any."},"appointment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The visit the message is about, when it is about one: named by whoever writes it, or taken from the event an automation answers. While it is queued, moving the visit or calling it off withdraws it (suppressed, with the reason), so the customer is never told a time that no longer holds. Fixed once written."},"appointment_start":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When that visit is booked to start now, or null for a message about no visit."},"subject":{"description":"The subject line, for email.","type":["string","null"]},"body":{"type":"string","description":"The message as it will be or was sent, with the details filled in."},"send_after":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"The earliest time to send it."},"sent_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was handed to the provider. Set automatically when it becomes sent."},"provider_reference":{"description":"The provider's id for the message, for tracing delivery.","type":["string","null"]},"delivery_status":{"anyOf":[{"type":"string","enum":["delivered","bounced","undelivered","complained"]},{"type":"null"}],"description":"What the service reported after accepting it: delivered; bounced (the address does not exist; it is marked undeliverable); undelivered (the carrier or mail server did not take it this time); complained (the customer marked it as spam; marketing consent is withdrawn). NULL until the service says. Only moves forward. Set by the system."},"delivery_status_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When delivery_status was last reported."},"delivery_detail":{"description":"What the service said about delivery, in its words.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","customer_id","customer_name","contact_point_id","to","channel","purpose","status","status_reason","message_template_id","template_name","automation_id","automation_name","event_id","appointment_id","appointment_start","subject","body","send_after","sent_at","provider_reference","delivery_status","delivery_status_at","delivery_detail","next","created_at"],"additionalProperties":false,"description":"One message to one customer address: waiting to be sent, sent, failed, or held back. It is both the queue the sender works from and the record of everything the tenant has said to a customer and everything it decided not to say."},"MessagePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of messages."},"MessageDispatch":{"type":"object","properties":{"sent":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"description":"Messages the email or text service accepted."},"failed":{"type":"array","items":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The message."},"why":{"type":"string","description":"What happened, and what to do about it."}},"required":["message_id","why"],"additionalProperties":false},"description":"Messages a service refused, with why. Final: write a new one to send it another way."},"suppressed":{"type":"array","items":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The message."},"why":{"type":"string","description":"What happened, and what to do about it."}},"required":["message_id","why"],"additionalProperties":false},"description":"Messages held back as they were taken to send, because consent or the address no longer allow them. Final."},"held":{"type":"array","items":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The message."},"why":{"type":"string","description":"What happened, and what to do about it."}},"required":["message_id","why"],"additionalProperties":false},"description":"Messages not sent now, with why: a service down or not ready to send (still queued, tried again on the next call), or one whose answer was lost (sending, until the service reports on it)."},"more":{"type":"boolean","description":"More are due than this call sent: call again."},"channels":{"type":"array","items":{"type":"string","enum":["email","sms"]},"description":"The channels the business has an email or text service connected for."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["sent","failed","suppressed","held","more","channels","next"],"additionalProperties":false,"description":"What one call to send the due messages did."},"CustomerReply":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"from_value":{"type":"string","description":"The number it came from, in E.164 form."},"contact_point_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's contact point with that number, when exactly one customer of the business's has it. NULL when none does, or several do (a shared household number)."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer, with contact_point_id."},"customer_name":{"description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization.","type":["string","null"]},"body":{"type":"string","description":"What the customer wrote."},"keyword":{"anyOf":[{"type":"string","enum":["stop","start","help"]},{"type":"null"}],"description":"stop (any opt-out word: every contact point of the business's with that number is denied consent), start (opted back in: service consent granted again), help, or NULL for anything else."},"received_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it arrived."},"handled_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When someone dealt with it. NULL while it waits."},"handled_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who dealt with it, when the key that marked it acts for a staff member."},"handled_by_staff_member_name":{"description":"Their name.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","from_value","contact_point_id","customer_id","customer_name","body","keyword","received_at","handled_at","handled_by_staff_member_id","handled_by_staff_member_name","next"],"additionalProperties":false,"description":"A text a customer sent the business's number: an answer to a reminder, a question, or STOP. Someone reads it and marks it handled. STOP, START and HELP are acted on when it arrives."},"CustomerReplyPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerReply"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of texts from customers."},"MessagingSetup":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string","enum":["email","sms"],"description":"email or sms."},"connected":{"type":"boolean","description":"True when the system sends this channel's service messages itself; false when an outside sender does and records them with updateMessage."},"provider":{"description":"Which service: resend for email, twilio for texts.","type":["string","null"]},"name":{"description":"The service's name as staff know it.","type":["string","null"]},"from_address":{"description":"For email: the address it is sent from, on the business's verified domain.","type":["string","null"]},"from_name":{"description":"For email: the name it is sent from, such as the business's. NULL sends the address alone.","type":["string","null"]},"reply_to_address":{"description":"For email: where a customer's reply goes, the business's own mailbox. NULL: to the from address.","type":["string","null"]},"sender_reference":{"description":"For texts: the messaging service (MG...) or phone number they are sent from, and the number customers text back.","type":["string","null"]},"events_url":{"description":"Where the service must send its news (delivery reports, and for texts the customers' replies): set it as the webhook in the service's dashboard.","type":["string","null"]}},"required":["channel","connected","provider","name","from_address","from_name","reply_to_address","sender_reference","events_url"],"additionalProperties":false},"description":"Email, then texts."}},"required":["channels"],"additionalProperties":false,"description":"Which channels have an email or text service connected, and who messages are sent as. Connecting one is the business's provider setup (scripts/connect-messaging.ts), not an operation."},"ReceiptDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"channel":{"type":"string","enum":["print","email"],"description":"print: printed, or shown to be printed. email: written as an email message to the customer (message_id), which goes out only where their consent allows."},"gift_receipt":{"type":"boolean","description":"A gift receipt: the goods without their prices, tenders or totals, for the person the goods are for. Printed only."},"is_reprint":{"type":"boolean","description":"Whether the same receipt had already been printed (a gift receipt counting only earlier gift receipts), so this copy is marked REPRINT. Always false for an email. Calculated."},"message_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an email, the message written: its status says whether it was sent or suppressed for want of consent. NULL for a print."},"message_status":{"anyOf":[{"type":"string","enum":["queued","sending","sent","failed","suppressed"]},{"type":"null"}],"description":"queued: waiting for send_after. sending: handed to the email or text service, its answer not yet recorded; set and cleared only by the system. sent: accepted by the provider. failed: the provider refused it, or never confirmed it; status_reason says why. suppressed: not sent, because consent does not allow it, the address is dead, or it was withdrawn (held back by hand, or its visit moved or was called off while it waited); set by the system when the message is queued or taken to send, and by an outside sender if marking it sent is refused because consent was withdrawn meanwhile. Sent, failed and suppressed are final. Null for a print. suppressed means the customer's consent did not allow it, and it was not sent."},"message_status_reason":{"description":"Why the message failed or was suppressed.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who printed or sent it."},"staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","channel","gift_receipt","is_reprint","message_id","message_status","message_status_reason","staff_member_id","staff_member_name","created_at"],"additionalProperties":false,"description":"One time a receipt was printed or emailed. Rows are only ever added."},"Receipt":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"receipt_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number printed on the receipt, after the branch code of where it was issued: BOI-1042. Issued in order with no gaps across the business. Calculated."},"receipt_label":{"type":"string","description":"The number as printed: the branch code, a dash and the number, BOI-1042."},"kind":{"type":"string","enum":["sale","deposit","payment","return","refund","account_payment","gift_card"],"description":"What the receipt is for. sale: goods handed over (with any payments taken at the same time). deposit: money taken for goods not yet handed over. payment: money taken toward goods already handed over. return: goods brought back. refund: money given back with no goods moving. account_payment: a payment on account. gift_card: a gift card sold or reloaded. Calculated from what it covers."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order the receipt is for. It covers every payment, refund, hand-over and return on the order that no earlier receipt covers (receipt_source); a payment that never arrived (a reversal) is on no receipt. NULL for a payment on account or a gift card."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number. Null when the receipt is not for an order."},"account_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment on account the receipt is for, with what it paid on orders in the same request. NULL for an order or a gift card."},"gift_card_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The sale or reload of a gift card the receipt is for. The card is shown by its number and what it holds after, never by its code. NULL for an order or a payment on account."},"gift_card_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The gift card sold or reloaded. Null for any other receipt."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number the business quotes for the card: on its receipts, its records and the phone. Calculated. It cannot spend the card, which takes the code. Null for any other receipt."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer the receipt is for: the one it was issued to, or, for a receipt issued with no customer, the customer since named on its order. Null when there is none: a sale with no customer named, or a gift card reload."},"customer_name":{"description":"The customer's name, or null when none is named.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where it was issued: its address and phone are printed on it, and its branch code before the number. The order's location, the payment's or the card sale's, unless another is named."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who issued it, printed on it as \"Served by\"."},"staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"issued_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was issued, at its location. Calculated."},"amount_charged":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the goods and fees it covers came to, tax included: negative for goods returned. 0.00 when it covers only money. For a gift card, what was loaded onto the card. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_paid":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The money it covers: payments positive, refunds negative. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_due_after":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"For an order: what was left to pay on the whole order once this receipt was issued (its total less everything paid, never below 0). For a payment on account: everything the customer still owed. NULL for a gift card. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For an order: what was left to pay on the whole order once this receipt was issued (its total less everything paid, never below 0). For a payment on account: everything the customer still owed. NULL for a gift card. Calculated."},"cash_tendered":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The cash the customer handed over, when they paid cash: at least the cash this receipt covers. Recorded only here, to print the change. NULL when no cash was tendered or it was not recorded. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cash the customer handed over, when they paid cash: at least the cash this receipt covers. Recorded only here, to print the change. NULL when no cash was tendered or it was not recorded."},"change_given":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash handed back: cash_tendered less the cash this receipt covers. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Cash handed back: cash_tendered less the cash this receipt covers. Calculated."},"content":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Everything printed on the receipt, frozen as it was issued: the business's name, address and words, the receipt's number and time, who served, the customer, the lines handed over or returned, fees, taxes by jurisdiction, tenders, the order's total and balance, and the store credit and account credit left. Never a gift card's code. Money is held as decimal strings. Its shape is content_version's. Calculated."},"content_version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Which shape content has, so a receipt issued under an older shape still prints. 1 for every receipt so far. Calculated."},"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/ReceiptDelivery"},"description":"Each time it was printed or emailed, the first first. Empty until it is."},"rendered":{"description":"The receipt as printed, in the format asked for. Null when no format was asked for.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","receipt_number","receipt_label","kind","sales_order_id","order_number","account_payment_id","gift_card_entry_id","gift_card_id","gift_card_number","customer_id","customer_name","location_id","location_name","staff_member_id","staff_member_name","issued_on","amount_charged","amount_paid","balance_due_after","cash_tendered","change_given","content","content_version","deliveries","rendered","next","created_at"],"additionalProperties":false,"description":"A receipt the business issued: for what happened on an order since its last receipt (a sale, a deposit, a payment, a return or a refund), for a payment on account, or for a gift card sold or reloaded. Numbered with no gaps, and built by the database from the records at the moment it was issued; what it says (content) never changes, so a reprint shows the same facts. Rows are only ever added. schema.org: an Invoice-like record of an Order's payment; none of its terms are used."},"ReceiptPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Receipt"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of receipts, newest first, each without its rendering."},"PrintedReceipt":{"type":"object","properties":{"receipt":{"$ref":"#/components/schemas/Receipt"},"delivery":{"description":"The print just recorded: is_reprint says whether it is marked as a copy.","$ref":"#/components/schemas/ReceiptDelivery"},"rendered":{"type":"string","description":"What to print, in the format asked for."}},"required":["receipt","delivery","rendered"],"additionalProperties":false,"description":"A receipt just printed, and what to print."},"StatementSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"statement_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number printed on the statement. Issued in order with no gaps across the business. Calculated."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer the statement is for."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"period_start":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day the statement covers: the day after the customer's previous statement ended. For a customer's first statement, the month that ends on period_end (the first of its month when period_end is a month's last day, else the day after the same date a month before) unless another day is named."},"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day the statement covers. It must be over at every location of the business, and later than the customer's previous statement."},"issued_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was issued. Calculated."},"opening_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed as the period began, less what the business held for them: the previous statement's closing balance, or for a first statement what they owed at the end of the day before period_start. Negative when the business held more of their money (deposits, account credit) than they owed. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"charges":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Goods handed over in the period, tax included, and fees kept (restocking and cancellation fees). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credits":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Goods returned in the period: what they sold for, tax included. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"money_received":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Payments in the period, however paid (cash, check, card, store credit, a gift card, a lender), and payments on account. Applying a payment on account to an order moves nothing here: the payment counted when it came in. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"money_returned":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money given back in the period (refunds, account credit refunded) and payments undone because they never arrived (a bounced check, a charge-back, a gift card the customer bought whose money never came). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"adjustments":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Changes since the previous statement to what it covered: for example an earlier sale with no customer named that has since been put on this customer. 0.00 unless something earlier changed. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closing_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed at the end of period_end, less what the business held for them: opening_balance + charges - credits - money_received + money_returned + adjustments, which is also amount_due - deposits_held - account_credit. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed at the end of the period for goods handed over and fees, and on account (a payment on account that did not arrive). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of amount_due past its due date at the end of the period. An order falls due its payment terms after the oldest goods on it still unpaid were handed over. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposits_held":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money paid on orders beyond what was handed over on them by the end of the period: held for goods still to come. Not set against amount_due. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"account_credit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money paid on account and not yet applied to an order, at the end of the period. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due_at_issue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed when the statement was issued: amount_due, less what they paid after the period and before it was issued, plus anything handed over to them since. What the statement asks them to pay, so a customer who paid after the period ends is not asked for it again. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["id","statement_number","customer_id","customer_name","customer_number","period_start","period_end","issued_on","opening_balance","charges","credits","money_received","money_returned","adjustments","closing_balance","amount_due","amount_overdue","deposits_held","account_credit","amount_due_at_issue"],"additionalProperties":false,"description":"A statement of one customer's account for a period: what they owed at its start, what was handed over, returned, paid and given back in it, and what they owed at its end, with what is overdue and by how long. Built by the database from dated records, never reissued: each opens on the previous one's closing balance, and anything earlier that changed since shows on the next one as an adjustment. Rows are only ever added."},"Statement":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"statement_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number printed on the statement. Issued in order with no gaps across the business. Calculated."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer the statement is for."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"period_start":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day the statement covers: the day after the customer's previous statement ended. For a customer's first statement, the month that ends on period_end (the first of its month when period_end is a month's last day, else the day after the same date a month before) unless another day is named."},"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day the statement covers. It must be over at every location of the business, and later than the customer's previous statement."},"issued_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was issued. Calculated."},"opening_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed as the period began, less what the business held for them: the previous statement's closing balance, or for a first statement what they owed at the end of the day before period_start. Negative when the business held more of their money (deposits, account credit) than they owed. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"charges":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Goods handed over in the period, tax included, and fees kept (restocking and cancellation fees). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credits":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Goods returned in the period: what they sold for, tax included. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"money_received":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Payments in the period, however paid (cash, check, card, store credit, a gift card, a lender), and payments on account. Applying a payment on account to an order moves nothing here: the payment counted when it came in. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"money_returned":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money given back in the period (refunds, account credit refunded) and payments undone because they never arrived (a bounced check, a charge-back, a gift card the customer bought whose money never came). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"adjustments":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Changes since the previous statement to what it covered: for example an earlier sale with no customer named that has since been put on this customer. 0.00 unless something earlier changed. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closing_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed at the end of period_end, less what the business held for them: opening_balance + charges - credits - money_received + money_returned + adjustments, which is also amount_due - deposits_held - account_credit. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed at the end of the period for goods handed over and fees, and on account (a payment on account that did not arrive). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of amount_due past its due date at the end of the period. An order falls due its payment terms after the oldest goods on it still unpaid were handed over. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposits_held":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money paid on orders beyond what was handed over on them by the end of the period: held for goods still to come. Not set against amount_due. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"account_credit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money paid on account and not yet applied to an order, at the end of the period. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due_at_issue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer owed when the statement was issued: amount_due, less what they paid after the period and before it was issued, plus anything handed over to them since. What the statement asks them to pay, so a customer who paid after the period ends is not asked for it again. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The store the statement is issued from: its address and phone are printed on it as where to pay and ask. Not where the sales on it were made, which can be any store. The business's first store still open (never a van) unless another is named."},"location_name":{"description":"That location's name.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who issued it."},"staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"aging":{"type":"object","properties":{"current":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Not yet past due at the end of the period. A decimal string with up to 2 decimal places, for example \"12.50\"."},"days_1_30":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"1 to 30 days past due. A decimal string with up to 2 decimal places, for example \"12.50\"."},"days_31_60":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"31 to 60 days past due. A decimal string with up to 2 decimal places, for example \"12.50\"."},"days_61_90":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"61 to 90 days past due. A decimal string with up to 2 decimal places, for example \"12.50\"."},"over_90":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"More than 90 days past due. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["current","days_1_30","days_31_60","days_61_90","over_90"],"additionalProperties":false,"description":"amount_due by how far past due it was at the end of the period: {\"current\", \"days_1_30\", \"days_31_60\", \"days_61_90\", \"over_90\"}, each a decimal string. Calculated."},"content":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Everything printed on the statement, frozen as it was issued: the business and the customer with their addresses, the period, each charge, return, payment and refund in it with the running balance, the orders still owed on with their due dates, the deposits held, and (since) what moved after the period up to the issue with what was owed then. Money is held as decimal strings. Its shape is content_version's. Calculated."},"content_version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Which shape content has. 1 for every statement so far. Calculated."},"rendered":{"description":"The statement as printed, in the format asked for. Null when no format was asked for.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","statement_number","customer_id","customer_name","customer_number","period_start","period_end","issued_on","opening_balance","charges","credits","money_received","money_returned","adjustments","closing_balance","amount_due","amount_overdue","deposits_held","account_credit","amount_due_at_issue","location_id","location_name","staff_member_id","staff_member_name","aging","content","content_version","rendered","next","created_at"],"additionalProperties":false,"description":"A statement of one customer's account for a period: what they owed at its start, what was handed over, returned, paid and given back in it, and what they owed at its end, with what is overdue and by how long. Built by the database from dated records, never reissued: each opens on the previous one's closing balance, and anything earlier that changed since shows on the next one as an adjustment. Rows are only ever added."},"StatementPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Statement"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of statements, newest first, each without its rendering."},"IssuedStatements":{"type":"object","properties":{"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day the statements cover."},"issued":{"type":"array","items":{"$ref":"#/components/schemas/StatementSummary"},"description":"The statements issued now, by customer name."},"already_issued":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers left out because they already have a statement through period_end or later."},"paid_up_since":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers left out because they owed at the end of the period but have paid it all since (only_owing). Send only_owing false to issue theirs too."}},"required":["period_end","issued","already_issued","paid_up_since"],"additionalProperties":false,"description":"The statements a batch issued."},"EmailedStatement":{"type":"object","properties":{"statement":{"$ref":"#/components/schemas/Statement"},"message_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The email message written."},"message_status":{"type":"string","enum":["queued","sending","sent","failed","suppressed"],"description":"queued: waiting for send_after. sending: handed to the email or text service, its answer not yet recorded; set and cleared only by the system. sent: accepted by the provider. failed: the provider refused it, or never confirmed it; status_reason says why. suppressed: not sent, because consent does not allow it, the address is dead, or it was withdrawn (held back by hand, or its visit moved or was called off while it waited); set by the system when the message is queued or taken to send, and by an outside sender if marking it sent is refused because consent was withdrawn meanwhile. Sent, failed and suppressed are final. suppressed means the customer's consent did not allow it."},"message_status_reason":{"description":"Why the message failed or was suppressed.","type":["string","null"]}},"required":["statement","message_id","message_status","message_status_reason"],"additionalProperties":false,"description":"A statement just emailed, and what became of the email."},"AccountCreditApplication":{"type":"object","properties":{"payment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The payment by the On account method that applied the credit to the order, or took it back off."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What it applied to the order; negative for what a reversal took back off it, which the customer owes again. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the order owes now for goods handed over: 0 when it is paid. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["payment_id","sales_order_id","order_number","amount","amount_due"],"additionalProperties":false,"description":"Account credit applied to one order, or taken back off it."},"AccountPayment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["received","refund","reversal"],"description":"received: money paid on account. refund: account credit given back. reversal: a payment on account that never arrived, undone."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer who paid, or was paid back."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid: cash, a check, a card or another method of the business's own. Not store credit, a lender or On account. A reversal goes back by the method of the payment it reverses."},"payment_method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive for money received, negative for money given back or a payment reversed. A refund is at most the customer's account credit; a reversal at most what is left of the payment it reverses. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the money was taken or given back. A reversal is at the location of the payment it reverses."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer the cash went into or came out of, for a payment by a cash method; needed for cash when the business keeps every dollar in a drawer (tenant_policy.cash_needs_drawer). A reversal of cash taken into a drawer comes back out of that drawer while it is open, and out of its safe once it is closed (NULL here). NULL for any other method. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The drawer's number, or null when the money went through none. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"reference":{"description":"The outside reference for the money: a check number, a card processor's transaction id, a card's last four digits. Never a full card number: one is refused. A reversal carries the reference of the payment it reverses unless it is given its own.","type":["string","null"]},"paid_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"note":{"description":"Anything worth recording, for example \"September statement\" or why the credit was refunded.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took or gave back the money."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"reverses_account_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Set when this row undoes an earlier payment on account that turned out not to be money: a check that bounced, a card payment charged back. The amount is negative, no more than is left of that payment, and goes back by its method. It takes back only that payment's money: what of it is still account credit, then what it paid on orders, the latest applied first, which are owed again whatever the credit limit. What is still not covered went back to the customer as money: it comes out of their other account credit, and beyond that they owe it on account (account_credit.owed). NULL for a payment or a refund."},"left_to_reverse":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For money received: how much of it is not yet reversed, which is the most reverseAccountPayment takes. 0.00 for a refund or a reversal. A decimal string with up to 2 decimal places, for example \"12.50\"."},"applications":{"type":"array","items":{"$ref":"#/components/schemas/AccountCreditApplication"},"description":"What this request applied to the customer's orders, oldest due first or as chosen; for a reversal, what it took back off them because the credit no longer covered it. Empty when it applied nothing."},"account_credit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The customer's account credit now. Money the customer has paid on account and not yet applied. Spent on their orders with the On account method (applyAccountCredit), or given back (refundAccountCredit). Never below zero, and 0 while they owe on account (owed). A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Everything the customer owes now. Everything the customer owes: for goods handed over, and on account (owed_on_account). A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_on_account":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of amount_due owed on account with no order behind it: a payment on account that did not arrive after its money had gone back to them as money, or a finance charge. takeAccountPayment collects it before anything else; it falls due like an order (amount_overdue, oldest_due_on), and the credit limit and overdue holds count it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"receipt_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The receipt issued for money received: print it with printReceipt or email it with emailReceipt. Null for a refund or a reversal."},"receipt_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That receipt's number, or null."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","kind","customer_id","customer_name","customer_number","payment_method_id","payment_method_name","amount","location_id","location_name","cash_drawer_session_id","cash_drawer_session_number","reference","paid_on","note","staff_member_id","staff_member_name","reverses_account_payment_id","left_to_reverse","applications","account_credit","amount_due","owed_on_account","receipt_id","receipt_number","next","created_at"],"additionalProperties":false,"description":"Money a customer paid toward what they owe without naming one order, or given back from their account credit. Positive when received; negative for a refund of account credit, or for a reversal of a payment that never arrived (a bounced check). What is received is applied to their orders by payments of the On account method, and the rest is their account credit. Rows are only ever added."},"AccountPaymentPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountPayment"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of payments on account, refunds of account credit and reversals."},"AccountCreditEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["received","refunded","reversed","applied","unapplied","gift_card_reversed"],"description":"received: money paid on account. refunded: account credit given back as money. reversed: a payment on account that never arrived, taken back out. applied: credit spent paying an order (a payment by On account). unapplied: money held on an order moved back into credit (a refund to On account), or an application taken back off its order because the payment that paid it was reversed. gift_card_reversed: what a gift card the customer bought had already spent when its sale was reversed, the money never having arrived (gift_card_entry_id); it comes out of their account credit, and what that does not cover they owe on account. finance_charge: a charge for paying late (finance_charge_id); it comes out of their account credit, and what that does not cover they owe on account. finance_charge_waived: what was still owed on a finance charge, forgiven; it pays that charge's part of what they owe on account."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer whose credit changed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive when credit is added or a finance charge is waived, negative when it is spent, refunded, reversed or charged: the change in the customer's account credit less what they owe on account. What comes in pays what is owed on account first. Only a reversal (of a payment on account, or of a gift card sale) or a finance charge adds to what is owed, by what neither the reversed payment's own credit, nor what it paid on orders, nor the customer's other credit covered. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_after":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The customer's account credit after this entry, in the order entries were applied (sequence). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"sequence":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The order the customer's entries were applied in: 1 for the first, the highest is the latest. Calculated."},"entered_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the money moved."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"account_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment on account, refund or reversal. Set for received, refunded and reversed."},"payment_method_name":{"description":"For received, refunded and reversed: the method the money came in or went back by. Null otherwise.","type":["string","null"]},"reference":{"description":"For received, refunded and reversed: the payment on account's reference, such as the check number. Null otherwise.","type":["string","null"]},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment by the On account method that applied the credit to an order, or moved money back off one. Set for applied and unapplied."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For applied and unapplied: the order the credit went onto or came back off. Null otherwise."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number, or null."},"gift_card_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For gift_card_reversed: the gift card whose sale was reversed. Null otherwise."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The gift card's number, or null."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who moved it."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","kind","customer_id","customer_name","amount","balance_after","sequence","entered_on","location_id","location_name","account_payment_id","payment_method_name","reference","payment_id","sales_order_id","order_number","gift_card_id","gift_card_number","staff_member_id","staff_member_name","created_at"],"additionalProperties":false,"description":"One change in a customer's account credit: money paid on account, applied to an order, moved back off an order, refunded or reversed. This is the account credit ledger: rows are only ever added, each written by the payment on account or the payment by the On account method that caused it."},"AccountCreditEntryPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountCreditEntry"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of account credit entries."},"AccountCreditApplied":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"applications":{"type":"array","items":{"$ref":"#/components/schemas/AccountCreditApplication"},"description":"What was applied, order by order. Empty when nothing was."},"account_credit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The customer's account credit left. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Everything the customer still owes. Everything the customer owes: for goods handed over, and on account (owed_on_account). A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_on_account":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of amount_due owed on account with no order behind it: a payment on account that did not arrive after its money had gone back to them as money, or a finance charge. takeAccountPayment collects it before anything else; it falls due like an order (amount_overdue, oldest_due_on), and the credit limit and overdue holds count it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["customer_id","customer_name","applications","account_credit","amount_due","owed_on_account","next"],"additionalProperties":false,"description":"Account credit applied to a customer's orders."},"FinanceChargeLine":{"type":"object","properties":{"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order owed on. NULL for what was owed on account."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number. Null for what was owed on account."},"owed_part_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The part of what the customer owed on account (account_debt.id): a payment on account that did not arrive, a gift card sale reversed, or, when the business charges on finance charges, an earlier finance charge. NULL for an order."},"amount_owed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What was owed on it when the charge was made. A decimal string with up to 2 decimal places, for example \"12.50\"."},"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"When it fell due."},"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The days of the month it was past due: from the later of due_on and the last day of the month before, to the month's end."},"interest":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":"amount_owed x the monthly percent x 12 / 365 x days, to four places. The charge rounds their sum. A decimal string with up to 4 decimal places, for example \"12.5000\"."}},"required":["sales_order_id","order_number","owed_part_id","amount_owed","due_on","days","interest"],"additionalProperties":false,"description":"One amount past due a finance charge was charged on: an order owed on, or a part of what the customer owed on account, with how many days of the month it was past due and what the rate came to on it. Written by the database with its charge; rows are only ever added."},"FinanceCharge":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"finance_charge_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number people quote for the charge, issued in order with no gaps across the business. Calculated."},"status":{"type":"string","enum":["owing","paid","waived"],"description":"owing: some of it is still owed (amount_due). paid: paid in full, by a payment on account or account credit. waived: what was still owed on it was forgiven (waiver)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer charged."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day of the month charged for. The month must be over at every location, and after any month the customer was charged for before."},"charged_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was charged, at its location: it falls due by the customer's payment terms from this day. Calculated."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the charge is booked: the location of the oldest amount it was charged on. Calculated."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"amount_past_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What was past due and charged on: the amounts owed on its lines, added up. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"monthly_percent":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"The rate it was charged at: the business's finance_charge_monthly_percent then. Calculated. A decimal string with up to 3 decimal places, for example \"12.500\"."},"interest":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The rate's charge: its lines' interest added up and rounded to the cent. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer was charged: interest, or the business's finance_charge_minimum then when that is more. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is still owed on it now. 0.00 once it is paid or waived. A decimal string with up to 2 decimal places, for example \"12.50\"."},"due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When what is still owed on it falls due: charged_on plus the customer's payment terms. Null when nothing is owed."},"days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days past due_on it is today. 0 when not yet due or nothing is owed."},"amount_waived":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What its waivers forgave, or 0.00. A decimal string with up to 2 decimal places, for example \"12.50\"."},"waiver":{"anyOf":[{"type":"object","properties":{"reason":{"type":"string","description":"Why, for example \"paid late because our statement went to the old address\"."},"waived_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who waived it."},"staff_member_name":{"description":"Who waived it, or null when none is recorded.","type":["string","null"]}},"required":["reason","waived_on","staff_member_id","staff_member_name"],"additionalProperties":false},{"type":"null"}],"description":"The latest waiver, or null when it has not been waived."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/FinanceChargeLine"},"description":"What it was charged on, oldest due first."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who made the charge."},"staff_member_name":{"description":"Who made the charge, or null when none is recorded.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","finance_charge_number","status","customer_id","customer_name","customer_number","period_end","charged_on","location_id","location_name","amount_past_due","monthly_percent","interest","amount","amount_due","due_on","days_overdue","amount_waived","waiver","lines","staff_member_id","staff_member_name","created_at","next"],"additionalProperties":false,"description":"A charge to one customer for paying late, for one month: what was past due at the month's end, charged at the business's rate for the days of the month it was past due (finance_charge_line), and at least its minimum. Owed on account like any debt with no order behind it (account_debt), it falls due by the customer's payment terms from the day it was charged. One per customer per month. Rows are only ever added; a waiver (finance_charge_waiver) forgives what is still owed on one."},"FinanceChargePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FinanceCharge"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of finance charges."},"FinanceChargeDue":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer who would be charged."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"amount_past_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What was past due and charged on: the amounts owed on its lines, added up. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"interest":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The rate's charge: its lines' interest added up and rounded to the cent. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What they would be charged: interest, or the minimum when that is more. A decimal string with up to 2 decimal places, for example \"12.50\"."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/FinanceChargeLine"},"description":"What they would be charged on, oldest due first. owed_part_id names a part of what they owe on account."}},"required":["customer_id","customer_name","customer_number","amount_past_due","interest","amount","lines"],"additionalProperties":false,"description":"What one customer would be charged for the month."},"FinanceChargesDue":{"type":"object","properties":{"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day of the month the charges are for."},"finance_charges":{"type":"boolean","description":"True when the business charges customers for paying late: assessFinanceCharges then charges each customer, once a month, on what they owe past due. False by default, so nobody is charged until the business decides to, at a rate (finance_charge_monthly_percent) or a flat amount (finance_charge_minimum) it chooses. Charges already made are still owed when it is turned off. Changing it needs credit.manage."},"finance_charge_monthly_percent":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"The finance charge, as a percent a month of what is past due: 1.5 is 18% a year. It is charged by the day, at this times 12 over 365 for each day an amount was past due in the month. 0 (the default) charges no rate: only finance_charge_minimum, a flat late fee. At most 10. Changing it needs credit.manage. A decimal string with up to 3 decimal places, for example \"12.500\"."},"finance_charge_minimum":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The least a customer is charged in a month when they are charged at all: with a rate, the larger of the two; with no rate, a flat monthly late fee. 0 (the default) charges no minimum. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"finance_charge_min_overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"A customer whose past-due amounts charged on add up to less than this is not charged that month, so a few dollars late never brings a minimum charge larger than itself. 0 (the default) charges any amount past due. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"finance_charge_grace_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days past due an amount must be, at the month's end, before it is charged on. 0 (the default) charges anything past due; 10 leaves an invoice 10 days late or less alone that month. Changing it needs credit.manage."},"finance_charge_compounds":{"type":"boolean","description":"True when an earlier finance charge still unpaid past its due date is charged on as well, as any other amount past due. False (the default): finance charges are never charged on. Changing it needs credit.manage."},"due":{"type":"array","items":{"$ref":"#/components/schemas/FinanceChargeDue"},"description":"Each customer who would be charged, by name."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What they would be charged, added up. A decimal string with up to 2 decimal places, for example \"12.50\"."},"already_charged":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers with something past due who were charged for this month, or a later one, already."},"below_min_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers left out because what is past due on them is less than finance_charge_min_overdue."},"holding_credit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers left out because they hold money paid on account: apply it to what they owe (applyAccountCredit)."}},"required":["period_end","finance_charges","finance_charge_monthly_percent","finance_charge_minimum","finance_charge_min_overdue","finance_charge_grace_days","finance_charge_compounds","due","total","already_charged","below_min_overdue","holding_credit"],"additionalProperties":false,"description":"What assessFinanceCharges would charge for a month, at the business's policy now, and who it would leave out."},"FinanceChargesAssessed":{"type":"object","properties":{"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day of the month the charges are for."},"charged":{"type":"array","items":{"$ref":"#/components/schemas/FinanceCharge"},"description":"The charges made, one per customer, by name."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What was charged, added up. A decimal string with up to 2 decimal places, for example \"12.50\"."},"already_charged":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers with something past due who were charged for this month, or a later one, already."},"below_min_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers left out because what is past due on them is less than finance_charge_min_overdue."},"holding_credit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers left out because they hold money paid on account: apply it to what they owe (applyAccountCredit)."}},"required":["period_end","charged","total","already_charged","below_min_overdue","holding_credit"],"additionalProperties":false,"description":"The month's finance charges, as made."},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"Who or what holds the key, for example \"Claude Desktop, front counter\"."},"kind":{"type":"string","enum":["session","agent","account","device"],"description":"session: issued when a person signs in with their password, or switches in with their PIN on a counter device; it acts for them and expires after the business's staff_session_hours (device_session_minutes for a switch). agent: issued for an AI agent or a program, acting for the person who issued it or for nobody with roles of its own. account: how an integration's account (a card processor, an email or text service) reports back; it changes only when the account's key_version moves, never through the API. device: a shared counter computer a manager enrolled; it acts for nobody, holds nothing, and can only list who may switch in there and switch a person in with their PIN. Fixed once the key exists."},"key_prefix":{"type":"string","description":"Leading characters of the key, safe to display so a holder can tell keys apart."},"key_last4":{"type":"string","description":"The last four characters of the key, safe to display beside key_prefix so a holder can tell keys apart."},"display":{"type":"string","description":"How to show the key to a person: its prefix and last four, for example rtv_AbC123…wXyZ."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The staff member this key acts for: what it writes is their doing, it may do what their roles allow, and it is revoked when they leave (a person who comes back is issued a new key). NULL for a key that acts for nobody in particular, such as a shared terminal or an integration. Fixed once the key exists: a key is never handed from one person to another."},"staff_member_name":{"description":"The name of the person it acts for, or null when it acts for nobody.","type":["string","null"]},"roles":{"type":"array","items":{"type":"object","properties":{"role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A role the key holds in its own right."},"role_name":{"type":"string","description":"The role's name, unique within the tenant."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The one location the role applies at. NULL when it applies everywhere."},"location_name":{"description":"The location's name, or null when held everywhere.","type":["string","null"]}},"required":["role_id","role_name","location_id","location_name"],"additionalProperties":false},"description":"Roles the key holds in its own right. A key that acts for a person also holds whatever that person's roles give; those are not listed here."},"is_read_only":{"type":"boolean","description":"For an agent key: true when it may only look things up. Every request but a read (and signing itself out) is refused before anything else about it is looked at, whatever its roles or its person hold; its roles only decide what it may see. False for every other key. Fixed once the key exists."},"status":{"type":"string","enum":["active","expired","revoked"],"description":"active: it works. expired: its expires_at has passed on the business's clock. revoked: it was revoked, or its person left. Neither an expired nor a revoked key ever works again."},"is_current":{"type":"boolean","description":"True for the key that made this request."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"expires_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the key stops working, on the business's clock. NULL for a key that does not expire. It can move earlier (a rotation sets the old key's), never later."},"revoked_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the key was revoked. NULL until then. Once set it never changes: a revoked key never works again."},"revoke_reason":{"anyOf":[{"type":"string","enum":["signed_out","revoked","password_reset","left","switched"]},{"type":"null"}],"description":"Why the key was revoked: signed_out (it signed itself out), revoked (by its holder or a manager, or its counter device was revoked), password_reset (its person's password was set or changed), left (its person left), switched (someone else switched in on its counter device). NULL until revoked."},"issued_by_api_key_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The key that issued this one. NULL when the system did: provisioning, or a sign-in. Fixed once the key exists."},"issued_by_api_key_name":{"description":"Who issued it: the issuing key's person and name, or the key's name when it acts for nobody.","type":["string","null"]},"replaces_api_key_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The key this one was issued to replace by a rotation. Fixed once the key exists."},"replaces_api_key_name":{"description":"The replaced key's name, which this one keeps.","type":["string","null"]},"account":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["card_processor","email","sms"],"description":"Which kind of integration's account."},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The account's name as staff know it."}},"required":["kind","id","name"],"additionalProperties":false},{"type":"null"}],"description":"For a key of kind account: the integration's account it reports back for. Such a key is never issued, rotated or revoked through the API. Null for every other key."},"device":{"anyOf":[{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"For a device key: the store the counter computer is at. People who hold a role there or everywhere switch in on it. NULL for every other key. Fixed once the key exists."},"location_name":{"type":"string","description":"The store's name."},"register_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a device key: the register (cash drawer) the counter computer takes cash into, at its store. NULL for none, and for every other key. Fixed once the key exists."},"register_name":{"description":"The register's name, or null for none.","type":["string","null"]},"card_reader_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a device key: the card reader the counter computer sends charges to. NULL for a separate card terminal, and for every other key. Fixed once the key exists."},"card_reader_name":{"description":"The card reader's name, or null for none.","type":["string","null"]}},"required":["location_id","location_name","register_id","register_name","card_reader_id","card_reader_name"],"additionalProperties":false},{"type":"null"}],"description":"For a key of kind device: the counter computer's store, register and card reader. Null for every other key."},"device_api_key_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a session a person switched into with their PIN: the counter's device key it was made on. It stops working when that device is revoked or expires. NULL for a session signed in with a password, and for every other key. Fixed once the key exists."},"device_api_key_name":{"description":"The name of the counter device the session was switched into on, or null.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","name","kind","key_prefix","key_last4","display","staff_member_id","staff_member_name","roles","is_read_only","status","is_current","created_at","expires_at","revoked_at","revoke_reason","issued_by_api_key_id","issued_by_api_key_name","replaces_api_key_id","replaces_api_key_name","account","device","device_api_key_id","device_api_key_name","next"],"additionalProperties":false,"description":"A bearer credential for the HTTP API, held by a person signed in, an agent or an integration acting for one tenant. The application role has no direct access: it authenticates through app.authenticate_api_key() and lists keys through app.api_keys(). Who a key acts for, what it may do, and how keys are issued and revoked are in db/migrations/0002_staff.sql. The secret is never part of this record."},"ApiKeyPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of API keys and the cursor for the next."},"IssuedApiKey":{"type":"object","properties":{"api_key":{"$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"The key itself. Shown in this response only and never again: the business keeps only its hash. Send it as 'Authorization: Bearer <secret>'. Keep it where only its holder can read it."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["api_key","secret","next"],"additionalProperties":false,"description":"A key just issued, with its secret, which is shown this once."},"RotatedApiKey":{"type":"object","properties":{"api_key":{"description":"The new key, which replaces the old one.","$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"The key itself. Shown in this response only and never again: the business keeps only its hash. Send it as 'Authorization: Bearer <secret>'. Keep it where only its holder can read it."},"replaced":{"description":"The old key, now expiring at the end of the overlap.","$ref":"#/components/schemas/ApiKey"},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["api_key","secret","replaced","next"],"additionalProperties":false,"description":"A key rotated: the new key with its secret, shown this once, and the old one it replaces."},"SignedIn":{"type":"object","properties":{"api_key":{"description":"The session key just issued. It acts for the person until it expires.","$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"The key itself. Shown in this response only and never again: the business keeps only its hash. Send it as 'Authorization: Bearer <secret>'. Keep it where only its holder can read it."},"staff_member":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"given_name":{"type":"string","description":"First name."},"family_name":{"type":"string","description":"Last name."},"roles":{"type":"array","items":{"type":"object","properties":{"role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A role the person holds."},"role_name":{"type":"string","description":"The role's name, unique within the tenant."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The one location the role applies at. NULL when it applies everywhere."},"location_name":{"description":"The location's name, or null when held everywhere.","type":["string","null"]}},"required":["role_id","role_name","location_id","location_name"],"additionalProperties":false},"description":"The person's roles, which the session holds through them (api_key.roles lists only a key's own, none for a session). getIdentity shows the permissions they come to."}},"required":["id","given_name","family_name","roles"],"additionalProperties":false,"description":"The person signed in."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["api_key","secret","staff_member","next"],"additionalProperties":false,"description":"A person signed in: their session key, with its secret shown this once."},"PasswordChanged":{"type":"object","properties":{"password_set_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the person last set their password. NULL until they have one: they cannot sign in yet."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["password_set_at","next"],"additionalProperties":false,"description":"A password changed. Every other session of the person's has ended."},"SignInCode":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"staff_member_name":{"type":"string","description":"The person the code is for."},"sign_in_code":{"type":"string","description":"The one-time code, shown this once. Give it to the person, in person or by a channel only they read: whoever has it can set their password."},"expires_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Until when the sign-in code a manager issued can set a password. NULL when there is none: it was used, or none was issued."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["staff_member_id","staff_member_name","sign_in_code","expires_at","next"],"additionalProperties":false,"description":"A one-time sign-in code, with which a person sets their password."},"AuditEvent":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"table_schema":{"type":"string","description":"Schema of the changed row's table: public for the core, or a module's schema. Changes to a module's tables are visible only while the tenant has the module."},"table_name":{"type":"string","description":"Table of the changed row."},"row_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id of the changed row."},"action":{"type":"string","enum":["insert","update"],"description":"insert or update. Rows are never deleted."},"changes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"For an insert, the new row as {column: value}. For an update, only the columns that changed, as {column: {from, to}}. Secret columns appear as \"[redacted]\"."},"request_id":{"description":"Id of the API request that caused the change, matching request_id in error responses and server logs. NULL outside a request.","type":["string","null"]},"actor_kind":{"type":"string","enum":["api_key","system"],"description":"What made the change: api_key (an agent or integration calling the API) or system (provisioning, migrations and background jobs)."},"actor":{"anyOf":[{"type":"object","properties":{"api_key_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The API key that made the change."},"key_name":{"type":"string","description":"Who or what holds the key, for example \"Claude Desktop, front counter\"."},"key_prefix":{"type":"string","description":"Leading characters of the key, safe to display so a holder can tell keys apart."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The staff member the key acts for, or null for a key that acts for nobody."},"staff_member_name":{"description":"That staff member's name.","type":["string","null"]}},"required":["api_key_id","key_name","key_prefix","staff_member_id","staff_member_name"],"additionalProperties":false},{"type":"null"}],"description":"Who made the change: the API key and the person it acts for. Null for a change the system made."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the change was made (UTC, ISO 8601)."}},"required":["id","table_schema","table_name","row_id","action","changes","request_id","actor_kind","actor","created_at"],"additionalProperties":false,"description":"One write to one row: who made it, through which request, and what changed. Written only by the audit trigger on each table, never by the application, and never changed afterwards. created_at is when the write happened."},"AuditEventPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditEvent"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of the audit trail."},"CodeErrorReceipt":{"type":"object","properties":{"status":{"type":"string","const":"received","description":"Always received: the fault is counted for Retavon's developers."}},"required":["status"],"additionalProperties":false,"description":"The fault was received."},"MovedLot":{"type":"object","properties":{"stock_lot_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lot."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known.","type":["string","null"]},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much of the lot: positive into stock, negative out of it, as on the movement. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["stock_lot_id","lot_code","expires_on","quantity"],"additionalProperties":false,"description":"How much of one lot one stock movement took or brought, for an item tracked by lot. A movement's lots add up to its quantity. Derived: written by the database with the movement, never changed."},"ChosenOption":{"type":"object","properties":{"group":{"type":"string","description":"What the choice is called, unique for the model whatever its case. Orders name the group by it."},"option":{"type":"string","description":"What the option is called, unique in its group whatever its case. Orders name the option by it."},"price_difference":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the option adds to the model's selling price, before tax: 0 for one included in the price, negative for one that costs less. Taken onto an order line when the option is chosen, so a later change does not reprice orders already written. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["group","option","price_difference"],"additionalProperties":false,"description":"One option in a configuration: the answer to one of the model's option groups. Written by the system with the configuration."},"GoodsComing":{"type":"object","properties":{"supply":{"type":"string","enum":["special_order","stock"],"description":"special_order: goods bought for this order line. stock: goods on order for the store, which this line is next in line for."},"purchase_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The purchase order bringing them. NULL when not_ordered."},"purchase_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"purchase_order_status":{"type":"string","enum":["draft","ordered"],"description":"draft: not yet sent to the vendor. ordered: sent, and the goods are expected."},"purchase_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The purchase order line bringing them. NULL when not_ordered."},"purchase_order_line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor ordered from. Fixed once the order has lines."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the goods are to be delivered to."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many of the order line this source covers. A decimal string with up to 3 decimal places, for example \"12.500\"."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The vendor's date for them. NULL when it has given none, or not_ordered."},"is_late":{"type":"boolean","description":"True when the vendor's date has passed at the purchase order's location and the goods have not all arrived."},"previous_expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the vendor gave before its latest change, so the customer can be told it moved. Null when the date has never moved."},"date_changed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date it was recorded, at the purchase order's location. Calculated. Of the latest change; null when the date has never moved."},"date_change_note":{"description":"Why the date moved, as the vendor said it, for whoever answers the customer next. Of the latest change; null when none was given.","type":["string","null"]}},"required":["supply","purchase_order_id","purchase_order_number","purchase_order_status","purchase_order_line_id","purchase_order_line_number","vendor_id","vendor_name","location_id","location_name","quantity","expected_on","is_late","previous_expected_on","date_changed_on","date_change_note"],"additionalProperties":false,"description":"Goods on a purchase order that an order line waits on, how many, and when they are expected."},"WaitingCustomer":{"type":"object","properties":{"supply":{"type":"string","enum":["special_order","stock"],"description":"special_order: goods bought for this order line. stock: goods on order for the store, which this line is next in line for."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer's order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line waiting."},"sales_order_line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"description":"The customer's name, or null for an order with none.","type":["string","null"]},"quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many of this line's goods the order line waits for. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["supply","sales_order_id","order_number","sales_order_line_id","sales_order_line_number","customer_id","customer_name","quantity"],"additionalProperties":false,"description":"A customer's order line waiting on a purchase order line's goods."},"TradeIn":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"trade_in_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["agreed","received","given_back","cancelled"],"description":"agreed: the allowance is agreed and the unit not yet taken. received: the unit is in the business's hands, in stock, and its allowance paid toward the order. given_back: received, then handed back to the customer after the sale was called off or returned; its allowance was refunded. cancelled: the customer kept the unit. Only agreed becomes received or cancelled, and only received becomes given_back."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The quote or order the unit is traded in toward. It must name the customer. Fixed once the trade exists."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"order_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location that made the sale: trade_in.appraise is held there."},"order_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is trading the unit in: the order's customer. Set by the system."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's equipment record of the unit, when there is one. Left out, it is filled in when the unit is received with this customer's record of the unit it comes in as, if they have one. That record is retired when the unit is received and restored if it is given back. Fixed once the trade is received."},"equipment_name":{"description":"The equipment's name, or null when the trade names none.","type":["string","null"]},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"What the unit comes into stock as, for example a \"Used spa\" item: a good tracked by serial number. Its tax class decides which lines a same_class credit applies to. Fixed once the trade is received."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"description":{"type":"string","description":"What the unit is, as the customer and the receipt see it, for example \"2009 Grandee\". Defaults to the year, brand and model, else the item's name."},"brand":{"description":"The unit's brand, as the customer describes it.","type":["string","null"]},"model":{"description":"The unit's model, as the customer describes it.","type":["string","null"]},"serial_number":{"description":"The serial number on the unit's data plate, uppercase. Confirmed when the unit is received, when it becomes the serialized unit's; with none known then, TRADE- and the trade's number.","type":["string","null"]},"year_made":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The year the unit was made, as the customer or the plate says."},"item_condition":{"type":"string","enum":["used","damaged"],"description":"used: worn but working. damaged: needs repair before it can be sold, or is only scrap. schema.org itemCondition."},"condition_notes":{"description":"What the appraisal found: cracks, a failed pump, missing cover.","type":["string","null"]},"allowance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer is allowed for the unit, paid toward the order when it is received, and what the unit comes into stock at. Set and changed only by someone who holds trade_in.appraise at the order's location, and only while the trade is agreed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"received_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the unit came into stock. Set when it is received; defaults to the order's location."},"received_location_name":{"description":"That location's name, or null until the unit is received.","type":["string","null"]},"received_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the unit was received, at the location it came into. Set by the system."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The serialized unit the old unit is in stock as: the one it was sold as, when it is the trade's item, else a new unit of the trade's item. Set by the system when it is received."},"unit_serial_number":{"description":"The serial number the unit is in stock under, or null until it is received.","type":["string","null"]},"unit_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the unit is in stock now. Null until it is received, and once it is sold, given back or disposed of."},"unit_location_name":{"description":"That location's name, or null when the unit is not in stock.","type":["string","null"]},"unit_status":{"anyOf":[{"type":"string","enum":["in_stock","sold","disposed","given_back","out_of_stock"]},{"type":"null"}],"description":"Where the unit is now: in_stock (to sell, write down or haul away), sold (sold_on_order_id names the order), disposed (disposeTradeIn), given_back (to the customer who traded it), or out_of_stock (taken out another way, such as a stock adjustment). Null until it is received."},"sold_on_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order the unit was sold on, when unit_status is sold; null otherwise."},"sold_on_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That order's number, or null."},"unit_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the unit is in stock at now: its allowance, less any write-down. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the unit is in stock at now: the allowance, less any write-down. Null until it is received, once it has left stock (unit_status other than in_stock), and for a key without cost.view."},"disposed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the unit was hauled away or scrapped, at the location it left. Set by the system."},"disposal_reason":{"description":"Why the unit left stock without being sold: hauled to the dump, scrapped for parts. Setting it takes the unit out of stock as an adjustment. Set once.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who appraised the unit."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"notes":{"description":"Free-form notes for staff.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","trade_in_number","status","sales_order_id","order_number","order_location_id","order_location_name","customer_id","customer_name","equipment_id","equipment_name","item_id","sku","item_name","description","brand","model","serial_number","year_made","item_condition","condition_notes","allowance","received_location_id","received_location_name","received_on","serialized_unit_id","unit_serial_number","unit_location_id","unit_location_name","unit_status","sold_on_order_id","sold_on_order_number","unit_cost","disposed_on","disposal_reason","staff_member_id","staff_member_name","notes","created_at","updated_at","next"],"additionalProperties":false,"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition."},"TradeInPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TradeIn"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of trade-ins."},"FinancingPromotion":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"financing_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender offering it."},"financing_lender_name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"name":{"type":"string","description":"The plan's name as staff offer it, unique within the lender."},"description":{"description":"What the customer should be told, for example that interest is charged from the purchase date if the balance is not paid within the promotion.","type":["string","null"]},"term_months":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many months the customer has to pay."},"apr_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The yearly interest rate the customer pays over the term, in percent: 0 for no interest, 9.99 for 9.99%. A decimal string with up to 3 decimal places, for example \"12.500\"."},"dealer_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The lender's fee to the business, as a percent of what is financed, for example 6.5. Longer promotions usually cost more. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fee_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A fixed fee to the business per financed sale, on top of dealer_fee_percent. 0 for none. A decimal string with up to 2 decimal places, for example \"12.50\"."},"minimum_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least the lender finances on this plan. 0 for no minimum. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_active":{"type":"boolean","description":"False once the lender no longer offers it: no new applications on it."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","financing_lender_id","financing_lender_name","name","description","term_months","apr_percent","dealer_fee_percent","dealer_fee_amount","minimum_amount","is_active","created_at","updated_at"],"additionalProperties":false,"description":"One financing plan a lender offers the business's customers, for example \"12 months, no interest\" or \"84 months at 9.99%\", with what it costs the business. An application is made on one; its terms are copied onto the application, so changing a promotion changes only applications made after."},"FinancingLender":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The lender as a vendor, when the business keeps it as one (it sends bills, or is paid). NULL when not."},"vendor_name":{"description":"The vendor's name, or null when the lender is not kept as a vendor.","type":["string","null"]},"deposit_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account the lender's payouts land in: an asset account of the business's own, such as its bank account. Changing it applies to payouts recorded from then on."},"deposit_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"deposit_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender's own payment method: take a financed payment with takePayment by this method."},"payment_method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"funding_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days the lender usually takes to pay out, counted from the financed payment, or from the hand-over when funds_after_hand_over. A payout later than that shows as overdue."},"funds_after_hand_over":{"type":"boolean","description":"True when the lender pays out only once the goods are handed over, so funding_days counts from the last hand-over on the order. False counts from the financed payment."},"fee_settlement":{"type":"string","enum":["withheld","billed"],"description":"How the lender takes its dealer fee. withheld (the default): it pays out the financed amount less the fee. billed: it pays out the whole amount and bills the fee separately; the fee is then owed to it (Lender Fees Payable) until the bill is paid in the accounting system."},"notes":{"description":"Anything staff should know: the dealer number, who to call, how to submit a sale.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the business no longer sells on this lender: no new applications or financed payments. What is already financed is still refunded, reversed and paid out."},"promotions":{"type":"array","items":{"$ref":"#/components/schemas/FinancingPromotion"},"description":"Its promotions, offered ones first, then by term."},"unfunded":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the lender owes now: financed through it and not yet paid out, over all its applications. A decimal string with up to 2 decimal places, for example \"12.50\"."},"overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it owes that is past its usual funding_days. How many days past expected_funding_on it is today at the order's location, while the lender still owes something. 0 otherwise. A decimal string with up to 2 decimal places, for example \"12.50\"."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","vendor_id","vendor_name","deposit_account_id","deposit_account_code","deposit_account_name","payment_method_id","payment_method_name","funding_days","funds_after_hand_over","fee_settlement","notes","is_active","promotions","unfunded","overdue","created_at","updated_at"],"additionalProperties":false,"description":"A consumer lender: a finance company that lends to the business's customers so they can buy, for example a card issuer's promotional plans or an installment lender. Not a floor-plan lender, which lends to the business itself against its stock (floor_plan_lender). Each has its own payment method for taking a financed payment, the promotions it offers, and the terms it pays out on. All of it is the business's own setup."},"FinancingLenderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FinancingLender"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of lenders."},"FinancingApplication":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order being financed: a quote or an open order with a customer."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer applying: the order's customer."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"financing_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender applied to. Taken from the promotion."},"financing_lender_name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender's payment method, to take the financed payment by."},"payment_method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"financing_promotion_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The promotion applied for. Can be changed until money has been taken on the application, to another of the same lender's."},"financing_promotion_name":{"type":"string","description":"The plan's name as staff offer it, unique within the lender."},"term_months":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The promotion's term when it was chosen. Copied from it."},"apr_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The promotion's yearly rate in percent when it was chosen. Copied from it. A decimal string with up to 3 decimal places, for example \"12.500\"."},"dealer_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The promotion's dealer fee in percent when it was chosen, used for the fee expected at payout. Copied from it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fee_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The promotion's fixed dealer fee when it was chosen. Copied from it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"minimum_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The promotion's minimum financed amount when it was chosen. Copied from it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_requested":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much the customer asked to finance. A decimal string with up to 2 decimal places, for example \"12.50\"."},"status":{"type":"string","enum":["applied","approved","declined","cancelled","funded"],"description":"applied: sent to the lender, no answer yet. approved: the lender agreed to finance up to amount_approved; payments by the lender's method can be taken against it. declined: the lender refused, or withdrew its approval. cancelled: the customer or the business dropped it, or its order was cancelled. funded: the lender has paid out everything financed on it. An application goes applied, then approved, declined or cancelled; approved to declined, cancelled, or funded, which the system records when the payout arrives."},"amount_approved":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most the lender agreed to finance. Set when approved; can be changed while approved, never below what has been financed on it. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most the lender agreed to finance. Set when approved; can be changed while approved, never below what has been financed on it."},"approval_expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day a financed payment can be taken against the approval. NULL when the lender set no end. Money already taken stands after it."},"is_expired":{"type":"boolean","description":"True when the approval's last day has passed: nothing more can be taken through it."},"available":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What can still be taken through it now: amount_approved less financed, while it is approved and not expired; else 0. A decimal string with up to 2 decimal places, for example \"12.50\"."},"financed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Taken as payment through the lender, less what went back to it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"funded":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the lender has paid out, less what it took back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"unfunded":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the lender still owes: financed less funded. Negative when the business owes the lender money back (a refund to it after it paid out). A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fees":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The dealer fees recorded on its payouts. A decimal string with up to 2 decimal places, for example \"12.50\"."},"expected_funding_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When the lender should have paid out by: funding_days after the last financed payment, or after the last hand-over on the order when the lender pays only once goods are handed over. NULL while that lender is still waiting for the hand-over."},"days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days past expected_funding_on it is today at the order's location, while the lender still owes something. 0 otherwise."},"monthly_payment_estimate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"An estimate of the customer's monthly payment on what is financed, or on the amount approved or asked for before anything is: amount over term at no interest, else the standard amortized payment, rounded up to the cent. Only an estimate: the lender's statement is what the customer pays. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"The lender's number for the application or the customer's account, as the lender quotes it.","type":["string","null"]},"applied_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was recorded, at the order's location. Calculated."},"decided_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date it was approved, declined or cancelled, at the order's location. Calculated."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took the application."},"staff_member_name":{"description":"Who took the application, or null when not recorded.","type":["string","null"]},"note":{"description":"Anything worth recording, for example what the lender asked for.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","sales_order_id","order_number","customer_id","customer_name","financing_lender_id","financing_lender_name","payment_method_id","payment_method_name","financing_promotion_id","financing_promotion_name","term_months","apr_percent","dealer_fee_percent","dealer_fee_amount","minimum_amount","amount_requested","status","amount_approved","approval_expires_on","is_expired","available","financed","funded","unfunded","dealer_fees","expected_funding_on","days_overdue","monthly_payment_estimate","reference","applied_on","decided_on","staff_member_id","staff_member_name","note","next","created_at","updated_at"],"additionalProperties":false,"description":"A customer's application to a lender to finance an order on one of its promotions, and the lender's answer. Staff record it as the lender gives it; no lender is called. An approved application is what a payment by the lender's method is taken against, up to the amount approved and while the approval lasts."},"FinancingApplicationPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FinancingApplication"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of financing applications."},"FinancingFunding":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"financing_application_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The application the payout is for. It must be approved or funded."},"financing_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender. Taken from the application."},"financing_lender_name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order financed. Taken from the application."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much of what the lender owes this settles, before its fee: positive for a payout, no more than it still owes on the application; negative when it takes money back, no more than it has paid out on it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fee":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The lender's fee on this payout, the same sign as amount (a negative fee is a fee given back). Left out, it is the promotion's fee on what is now paid out less the fees already recorded on the application. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_received":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What actually moved into or out of the deposit account: amount less the fee when the lender withholds it, the whole amount when it bills the fee. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fee_settlement":{"type":"string","enum":["withheld","billed"],"description":"How the lender took its fee on this payout: withheld or billed. Copied from the lender."},"deposit_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account the money went into or came out of. Copied from the lender."},"deposit_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"funded_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the money arrived or left, as on the lender's statement or the bank's. Not after today at the order's location, and not in a closed period. Defaults to today."},"reference":{"description":"The lender's reference for the payout or the deposit, to match it to the bank.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who recorded it."},"staff_member_name":{"description":"Who recorded it, or null when not recorded.","type":["string","null"]},"note":{"description":"Anything worth recording, for example why the lender took money back.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","financing_application_id","financing_lender_id","financing_lender_name","sales_order_id","order_number","customer_name","amount","dealer_fee","amount_received","fee_settlement","deposit_account_id","deposit_account_name","funded_on","reference","staff_member_id","staff_member_name","note","created_at"],"additionalProperties":false,"description":"What a lender paid out to the business on one financing application, or took back from it. A ledger: rows are only ever added. A payout clears what the lender owed (Lender Receivable), brings the money into the lender's deposit account and books the dealer fee as Financing Fees; a negative row is the lender taking money back, after a refund to it or a dispute."},"FinancingFundingPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FinancingFunding"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of lender payouts."},"FinancingFundingBatch":{"type":"object","properties":{"financing_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender. Taken from the application."},"financing_lender_name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The payout's rows together, before fees. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fees":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Their dealer fees together. A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_received":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What moved into the deposit account, to match to the bank. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fundings":{"type":"array","items":{"$ref":"#/components/schemas/FinancingFunding"},"description":"The rows recorded, one per sale."}},"required":["financing_lender_id","financing_lender_name","amount","dealer_fees","amount_received","fundings"],"additionalProperties":false,"description":"A lender's payout, as recorded."},"FinancingReport":{"type":"object","properties":{"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the period."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day of the period."},"lenders":{"type":"array","items":{"type":"object","properties":{"financing_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender."},"financing_lender_name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"unfunded":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it owes now, over all its applications. A decimal string with up to 2 decimal places, for example \"12.50\"."},"overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it owes now that is past its usual funding_days. A decimal string with up to 2 decimal places, for example \"12.50\"."},"overdue_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many applications that is on."},"oldest_days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The most days past due of any of them. 0 when none is."},"financed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Taken as payment through it in the period, less what went back to it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"funded":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it paid out in the period, less what it took back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fees":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The dealer fees on its payouts in the period. A decimal string with up to 2 decimal places, for example \"12.50\"."},"approvals_expiring":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Approved applications with room left whose approval runs out within expiring_within_days."}},"required":["financing_lender_id","financing_lender_name","unfunded","overdue","overdue_count","oldest_days_overdue","financed","funded","dealer_fees","approvals_expiring"],"additionalProperties":false},"description":"One row per lender with anything to report, by name."}},"required":["from","to","lenders"],"additionalProperties":false,"description":"What lenders owe, how late they are, and what financing cost."},"FinancingEstimate":{"type":"object","properties":{"financing_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender."},"financing_lender_name":{"type":"string","description":"The lender's name as staff know it, unique within the tenant. Its payment method carries the same name."},"financing_promotion_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The promotion."},"financing_promotion_name":{"type":"string","description":"The plan's name as staff offer it, unique within the lender."},"description":{"description":"What the customer should be told, for example that interest is charged from the purchase date if the balance is not paid within the promotion.","type":["string","null"]},"term_months":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many months the customer has to pay."},"apr_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The yearly interest rate the customer pays over the term, in percent: 0 for no interest, 9.99 for 9.99%. A decimal string with up to 3 decimal places, for example \"12.500\"."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The amount the estimate is for. A decimal string with up to 2 decimal places, for example \"12.50\"."},"qualifies":{"type":"boolean","description":"False when the amount is below the promotion's minimum_amount."},"minimum_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least the lender finances on this plan. 0 for no minimum. A decimal string with up to 2 decimal places, for example \"12.50\"."},"monthly_payment_estimate":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"An ESTIMATE of the monthly payment: amount over term at no interest, else the standard amortized payment, rounded up to the cent. Say so to the customer: the lender's own terms and statement decide what they pay. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total_of_payments_estimate":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The monthly estimate times the term: what the customer pays in all. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dealer_fee_estimate":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the business would pay the lender on this amount: the promotion's dealer fee. Not for the customer. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["financing_lender_id","financing_lender_name","financing_promotion_id","financing_promotion_name","description","term_months","apr_percent","amount","qualifies","minimum_amount","monthly_payment_estimate","total_of_payments_estimate","dealer_fee_estimate"],"additionalProperties":false,"description":"What one promotion would cost the customer a month."},"FinancingEstimates":{"type":"object","properties":{"is_estimate":{"type":"boolean","const":true,"description":"Always true: these are estimates, not the lender's offer."},"data":{"type":"array","items":{"$ref":"#/components/schemas/FinancingEstimate"},"description":"One per promotion offered, cheapest monthly payment first."}},"required":["is_estimate","data"],"additionalProperties":false,"description":"Monthly payment estimates for the promotions on offer."},"GiftCardEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["issue","reload","promotional","redemption","refund","void","transfer_out","transfer_in","cash_out","write_off","reversal"],"description":"issue: the card sold, its first value. reload: more value sold onto it. promotional: a card given away, at the business's cost (ledger_account_id). redemption: spent paying an order (payment_id). refund: money from an order put back onto it (payment_id). void: a sale or reload taken back the same day, the money given back by the way it came (related_entry_id). reversal: a sale or reload whose money never arrived (a bounced check, a chargeback), on any day: what the card still holds of it comes off, and the rest, already spent, is owed by the buyer (owed; related_entry_id). transfer_out, transfer_in: what it held moved to a replacement card. cash_out: what it held paid out as money, where the business allows small balances to be. write_off: what it held taken off it: breakage, or unclaimed property owed to the state."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive when value goes onto the card, negative when it comes off. Nothing takes a card below zero. 0 for a move to a replacement of a blocked card that held nothing. For a reversal, what came off the card of the sale reversed (sent as all of the sale, negative), and 0 when it had all been spent. Calculated for a reversal. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_after":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the card held after this entry, in the order entries were applied (sequence). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"loaded":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What this entry put on cards as new value, counted toward the business's daily limit per card (tenant_policy.gift_card_max_load): all of a sale, a reload or a promotion, less a void, and the part of a refund beyond the card value its order held (gift_card_value), which came from money the order took. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For a reversal: what of the sale reversed had already been spent or paid out, and is now owed by the card's buyer on account (owed_by_customer_id; account_credit, account_debt). 0 for every other kind. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"written_back":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For a reversal: what of the sale reversed had been written off the card (breakage, or unclaimed property), now taken back off the account it was written off to, since no income is kept, and nothing is owed to the state, from money never received. For unclaimed property that debits the liability account it was written off to; if the business has already paid it to the state, its accountant adjusts that account (a claim back from the state, or an expense). 0 for every other kind. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"sequence":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The order the card's entries were applied in: 1 for the first, the highest is the latest. Calculated."},"entered_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where it happened. A void is where the sale was."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who did it."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"payment_method_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a sale, reload, void, reversal or cash-out: how the money came in or went back. Cash, a check, a card or another method of the business's own; never store credit, account credit, a lender or a gift card. A void and a reversal go back by the method of the sale, and a reversal never by cash, which cannot bounce."},"payment_method_name":{"description":"The method's name, or null for an entry with none.","type":["string","null"]},"cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer the cash went into or came out of, for a cash method; needed for cash when the business keeps every dollar in a drawer (tenant_policy.cash_needs_drawer). A void of a sale whose cash went into a drawer takes it back out of that drawer while it is open (named or not), and out of the safe once it is closed (NULL here). NULL for any other method. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The drawer's number, or null. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment by the Gift card method that spent the card, or the refund that put money back onto it. Set for redemption and refund only."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For redemption and refund: the order the card paid or was refunded on. Null otherwise."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number, or null."},"related_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a void or a reversal, the sale or reload it takes back (for a reversal, on this card or a card it replaced). For transfer_in, the transfer_out on the card replaced."},"ledger_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The account the money landed in or came out of: for cash through a drawer, Cash in Drawers; for a void's cash taken back out of the safe, Cash in Safe; otherwise the account the entry posted to (the money account, the promotion's expense account, Gift Card Breakage, or the unclaimed property account). Null for an entry that posts nothing of its own."},"ledger_account_name":{"description":"The account's code and name, or null.","type":["string","null"]},"write_off_reason":{"anyOf":[{"type":"string","enum":["breakage","escheat"]},{"type":"null"}],"description":"For a write-off: breakage (taken into income after the business's dormancy period) or escheat (owed to the state as unclaimed property)."},"reference":{"description":"The outside reference for the money: a card processor's transaction id, a card's last four digits, a check number. Never a full card number: one is refused.","type":["string","null"]},"note":{"description":"Why. Required for a promotion, a void, a reversal, a write-off and a replacement.","type":["string","null"]},"receipt_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The receipt issued for a sale or reload: print it with printReceipt or email it with emailReceipt. Null for any other entry."},"receipt_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That receipt's number, or null."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","kind","amount","balance_after","loaded","owed","written_back","sequence","entered_on","location_id","location_name","staff_member_id","staff_member_name","payment_method_id","payment_method_name","cash_drawer_session_id","cash_drawer_session_number","payment_id","sales_order_id","order_number","related_entry_id","ledger_account_id","ledger_account_name","write_off_reason","reference","note","receipt_id","receipt_number","created_at"],"additionalProperties":false,"description":"One change in what a gift card holds: sold, reloaded, given as a promotion, spent on an order, refunded onto, its sale voided or reversed, moved to a replacement card, cashed out, or written off. This is the card's ledger: rows are only ever added. The application writes sales, reloads, promotions, voids, reversals, cash-outs and write-offs; spending and refunds are written by the payment by the Gift card method, and moves by replacing the card."},"GiftCardSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"card_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number the business quotes for the card: on its receipts, its records and the phone. Calculated. It cannot spend the card, which takes the code."},"code_last4":{"description":"The last four characters of the card's code, to tell cards apart: \"the card ending 4821\". Shown only to those who hold giftcard.manage where the card was sold. Calculated. Null for any other API key: part of a code helps whoever would guess the rest.","type":["string","null"]},"code_origin":{"type":"string","enum":["generated","printed"],"description":"generated: the system made the code (16 random digits) and showed it once when the card was made. printed: the code came from the business's card stock, with a PIN."},"has_pin":{"type":"boolean","description":"Whether the card has a PIN, which must be sent with its code."},"status":{"type":"string","enum":["active","blocked","closed"],"description":"active: it can be spent and reloaded. blocked: reported lost or stolen, or held for a dispute; nothing can be spent from it until it is unblocked or replaced, and what it holds is still owed. closed: replaced, written off, cashed out or its sale voided; it holds nothing and is never used again."},"status_note":{"description":"Why the card is blocked, for example \"Reported lost by the purchaser, 2026-11-02\".","type":["string","null"]},"balance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the card holds: the sum of its entries. Never below zero. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"last_activity_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date of its latest entry: sold, reloaded, spent, refunded onto. A dormant card's balance may be taken as breakage only after the business's dormancy period (tenant_policy.gift_card_breakage_after_days). Calculated."},"pin_locked":{"type":"boolean","description":"Whether wrong PINs have locked the card: five lock it for fifteen minutes, ten until someone who holds giftcard.manage clears it. Shown only to an API key that holds giftcard.manage where the card was sold; false for any other."},"pin_locked_until":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"Until when the card is locked, UTC, ISO 8601. Null when it is not locked, or is locked until cleared (pin_locked true), or the key may not see it."},"issued_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the card was sold or given."},"issued_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"purchaser_customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer who bought or was given the card, when the business knows. The card is not theirs to spend: whoever holds the code spends it. They owe what was spent of a sale whose money never arrived (a reversal), so it is changed only with giftcard.manage, and never once a sale or reload on the card (or a card it replaced) was paid by a method other than cash."},"purchaser_customer_name":{"description":"The customer's name, or null.","type":["string","null"]},"recipient_name":{"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","card_number","code_last4","code_origin","has_pin","status","status_note","balance","last_activity_on","pin_locked","pin_locked_until","issued_location_id","issued_location_name","purchaser_customer_id","purchaser_customer_name","recipient_name","next","created_at","updated_at"],"additionalProperties":false,"description":"A gift card the business sold or gave away: whoever holds its code can spend what it holds, here, as payment. Its code is never stored, only a hash the application cannot read; it is shown once, when the card is made. What it holds is kept by its ledger (gift_card_entry). schema.org: a GiftCard-like Offer of stored value; none of its terms apply. Its code is never shown."},"GiftCard":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"card_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number the business quotes for the card: on its receipts, its records and the phone. Calculated. It cannot spend the card, which takes the code."},"code_last4":{"description":"The last four characters of the card's code, to tell cards apart: \"the card ending 4821\". Shown only to those who hold giftcard.manage where the card was sold. Calculated. Null for any other API key: part of a code helps whoever would guess the rest.","type":["string","null"]},"code_origin":{"type":"string","enum":["generated","printed"],"description":"generated: the system made the code (16 random digits) and showed it once when the card was made. printed: the code came from the business's card stock, with a PIN."},"has_pin":{"type":"boolean","description":"Whether the card has a PIN, which must be sent with its code."},"status":{"type":"string","enum":["active","blocked","closed"],"description":"active: it can be spent and reloaded. blocked: reported lost or stolen, or held for a dispute; nothing can be spent from it until it is unblocked or replaced, and what it holds is still owed. closed: replaced, written off, cashed out or its sale voided; it holds nothing and is never used again."},"status_note":{"description":"Why the card is blocked, for example \"Reported lost by the purchaser, 2026-11-02\".","type":["string","null"]},"balance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the card holds: the sum of its entries. Never below zero. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"last_activity_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date of its latest entry: sold, reloaded, spent, refunded onto. A dormant card's balance may be taken as breakage only after the business's dormancy period (tenant_policy.gift_card_breakage_after_days). Calculated."},"pin_locked":{"type":"boolean","description":"Whether wrong PINs have locked the card: five lock it for fifteen minutes, ten until someone who holds giftcard.manage clears it. Shown only to an API key that holds giftcard.manage where the card was sold; false for any other."},"pin_locked_until":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"Until when the card is locked, UTC, ISO 8601. Null when it is not locked, or is locked until cleared (pin_locked true), or the key may not see it."},"issued_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the card was sold or given."},"issued_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"purchaser_customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer who bought or was given the card, when the business knows. The card is not theirs to spend: whoever holds the code spends it. They owe what was spent of a sale whose money never arrived (a reversal), so it is changed only with giftcard.manage, and never once a sale or reload on the card (or a card it replaced) was paid by a method other than cash."},"purchaser_customer_name":{"description":"The customer's name, or null.","type":["string","null"]},"recipient_name":{"description":"Who the card is for, as the purchaser said, for example \"Grandma Ruth\". Only a label.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"entries":{"type":"array","items":{"$ref":"#/components/schemas/GiftCardEntry"},"description":"Everything that has changed what the card holds, the first first."}},"required":["id","card_number","code_last4","code_origin","has_pin","status","status_note","balance","last_activity_on","pin_locked","pin_locked_until","issued_location_id","issued_location_name","purchaser_customer_id","purchaser_customer_name","recipient_name","next","created_at","updated_at","entries"],"additionalProperties":false,"description":"A gift card the business sold or gave away: whoever holds its code can spend what it holds, here, as payment. Its code is never stored, only a hash the application cannot read; it is shown once, when the card is made. What it holds is kept by its ledger (gift_card_entry). schema.org: a GiftCard-like Offer of stored value; none of its terms apply. Its code is never shown."},"GiftCardPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/GiftCardSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of gift cards, never with their codes."},"GiftCardChanged":{"type":"object","properties":{"gift_card":{"$ref":"#/components/schemas/GiftCard"},"entry":{"description":"The entry this request wrote (or, for a repeat, the one it wrote the first time): its amount is what went onto the card (positive) or came off it (negative).","$ref":"#/components/schemas/GiftCardEntry"}},"required":["gift_card","entry"],"additionalProperties":false,"description":"A gift card, and the entry that just changed what it holds."},"GiftCardIssued":{"type":"object","properties":{"gift_card":{"$ref":"#/components/schemas/GiftCard"},"entry":{"description":"The new card's first entry: the sale, the promotion, or the move in from the card it replaces.","$ref":"#/components/schemas/GiftCardEntry"},"code":{"description":"The card's code, when the system made it up: give it to the customer now, written on the card or the receipt. It is shown this once and never again: a repeat of the request answers null, and a code that was lost is replaced with replaceGiftCard. Null for a code from the card stock.","type":["string","null"]}},"required":["gift_card","entry","code"],"additionalProperties":false,"description":"A gift card just sold, given or made to replace another."},"PackageComponentChoice":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item the customer may choose. Not the slot's own item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"item_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The item's regular price now, or null when it has none."},"price_difference":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What choosing it adds to the package price, before tax: 0 for a like-for-like choice, negative for one that costs less. Taken onto the order when it is chosen. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_active":{"type":"boolean","description":"False once it is no longer offered in the slot. Orders that have it keep it."}},"required":["item_id","sku","item_name","item_price","price_difference","is_active"],"additionalProperties":false,"description":"Another item a customer may have in a package slot instead of the slot's own: the cover in another color, the steps in another finish, a bigger model."},"PackageComponent":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What the slot is called, unique in the package whatever its case, for example Cover. Orders name the slot by it."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where the slot comes in the package, smallest first: the order of its lines on an order."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item the slot comes with unless the customer chooses another."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"item_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The item's regular price now, or null when it has none."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item, in its unit: 1 for a serial-tracked item. A decimal string with up to 3 decimal places, for example \"12.500\"."},"can_drop":{"type":"boolean","description":"True when the customer may leave the slot out, for example steps they already have. What that does to the package price is the business's policy (package_drop_credit). False for a slot the package is not sold without."},"is_main":{"type":"boolean","description":"True for the slot the package is built around, the spa: the one a package discount comes off first when the business shares discounts that way (package_discount_allocation main_component). At most one active slot per package."},"is_active":{"type":"boolean","description":"False once the slot is no longer part of the package. Orders that already have it keep it."},"choices":{"type":"array","items":{"$ref":"#/components/schemas/PackageComponentChoice"},"description":"The other items the customer may have in the slot instead."}},"required":["id","name","position","item_id","sku","item_name","item_price","quantity","can_drop","is_main","is_active","choices"],"additionalProperties":false,"description":"One slot in a package: the spa, the cover, the steps, the delivery. The slot comes with its own item unless the customer chooses another the slot offers (package_component_choice). schema.org: TypeAndQuantityNode."},"Package":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","description":"The business's own code for the package, uppercase, unique within the tenant, as staff quote it."},"name":{"type":"string","description":"What the package is called on the order and in advertising."},"description":{"description":"Longer description for quotes and the web store.","type":["string","null"]},"price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The one price the package is advertised at, before tax, with every slot's own item. A choice in a slot can add to it. Taken onto an order when the package is added, so a later change does not reprice orders already written. A decimal string with up to 2 decimal places, for example \"12.50\"."},"regular_price":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the slots' own items come to at their regular prices now, bought one by one: price less this is what the package saves. Null when one of them has no price."},"is_active":{"type":"boolean","description":"False once the package is no longer offered. Orders that have it keep it."},"components":{"type":"array","items":{"$ref":"#/components/schemas/PackageComponent"},"description":"The slots, in position order, retired ones last. On an order each active slot is a line."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","code","name","description","price","regular_price","is_active","components","created_at","updated_at"],"additionalProperties":false,"description":"Goods and services the business advertises and sells together at one price, for example a spa with its cover, cover lifter, steps, starter chemicals and delivery. Each slot is a package_component; on an order each becomes an ordinary line, so every component is stocked, taxed, handed over and returned on its own. schema.org: ProductCollection."},"PackagePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Package"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of packages and the cursor for the next."},"SalesOrderPackageComponent":{"type":"object","properties":{"package_component_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The slot."},"component_name":{"type":"string","description":"What the slot is called, unique in the package whatever its case, for example Cover. Orders name the slot by it."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item in the slot: the slot's own, or a choice it offers. Changing it puts the new item on a new line and drops the old one, while nothing in the slot has been handed over."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item, as the slot said when the package was added. Set by the system. A decimal string with up to 3 decimal places, for example \"12.500\"."},"can_drop":{"type":"boolean","description":"True when the customer may leave the slot out, for example steps they already have. What that does to the package price is the business's policy (package_drop_credit). False for a slot the package is not sold without."},"is_dropped":{"type":"boolean","description":"True when the customer left the slot out, which the slot must allow (can_drop): its line is dropped. Set back to false to have it after all, while the package can still change."},"price_difference":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the item chosen adds to the package price, as the choice said when it was made: 0 for the slot's own item. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},"item_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The item's regular price per unit when it was put in the slot: with the line's options, what the package price is shared by. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},"drop_credit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What leaving the slot out, or calling off the rest of it once part was handed over, took off the package price, where the business credits dropped components (package_drop_credit share), otherwise 0: what that part was priced at in the package, but never more than its share of the package price by regular prices. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},"sales_order_line_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order line carrying the item now. NULL while the slot is left out and has never had a line. Set by the system."},"line_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That line's number on the order, or null when it has none."},"extended_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The slot's share of the package price: its line's extended_price. Null when it has no line."}},"required":["package_component_id","component_name","item_id","sku","item_name","quantity","can_drop","is_dropped","price_difference","item_price","drop_credit","sales_order_line_id","line_number","extended_price"],"additionalProperties":false,"description":"What the customer is getting in one slot of a package on an order: the slot's own item or one of its choices, or nothing when they left it out. Made by the system for every slot when the package is added."},"SalesOrderPackage":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"package_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the package on the order, starting at 1. Assigned automatically."},"package_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The package sold. Fixed once it is on the order."},"package_code":{"type":"string","description":"The business's own code for the package, uppercase, unique within the tenant, as staff quote it."},"package_name":{"type":"string","description":"The package's name when it was added. Set by the system."},"base_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The package's advertised price when it was added. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},"list_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the package comes to before any discount given on this order: base_price, plus the price differences of the choices made and of the options chosen on its components, less what dropped components took off (by the business's policy, package_drop_credit). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"discount_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money taken off this package on this order, beyond the package's own pricing. At most list_price. A decimal string with up to 2 decimal places, for example \"12.50\"."},"price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer pays for the package before tax: list_price less discount_amount. Shared among its component lines. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"returned_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What returns of its goods have given back, before tax. 0.00 while none of it has come back; once any has, the package is fixed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_removed":{"type":"boolean","description":"True once the package was taken off the order before any of it was handed over: its lines are dropped and its price is 0. Final; add the package again to sell it."},"components":{"type":"array","items":{"$ref":"#/components/schemas/SalesOrderPackageComponent"},"description":"Each slot and what is in it, in the package's order."}},"required":["id","package_number","package_id","package_code","package_name","base_price","list_price","discount_amount","price","returned_amount","is_removed","components"],"additionalProperties":false,"description":"A package sold on a quote or order, at the price agreed for the whole. Its components are ordinary lines on the order, and the price is shared among them by their regular prices to the cent: each line's extended_price is its share, and the shares add up to price."},"PackageSales":{"type":"object","properties":{"date_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day counted."},"date_to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day counted."},"packages":{"type":"array","items":{"type":"object","properties":{"package_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The package sold. Fixed once it is on the order."},"package_code":{"type":"string","description":"The business's own code for the package, uppercase, unique within the tenant, as staff quote it."},"package_name":{"type":"string","description":"What the package is called on the order and in advertising."},"sold":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Packages on orders the customer agreed to in the period, not cancelled or taken off."},"delivered":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of those, how many have had every component kept handed over, and none of it back."},"returned":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of those, how many have had goods come back."},"booked_amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What those packages were sold for, before tax. A decimal string with up to 2 decimal places, for example \"12.50\"."},"returned_amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What returns of their goods have given back of that, before tax. booked_amount less this is what customers kept. A decimal string with up to 2 decimal places, for example \"12.50\"."},"revenue_fulfilled":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What their goods handed over have earned so far, less what came back, before tax. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost_fulfilled":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods handed over cost the business. Null without cost.view."},"components":{"type":"array","items":{"type":"object","properties":{"package_component_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The slot."},"component_name":{"type":"string","description":"What the slot is called, unique in the package whatever its case, for example Cover. Orders name the slot by it."},"kept":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of the packages sold kept the slot and have not returned all of it."},"dropped":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many left it out."},"returned":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many brought all of it back after it was handed over."},"attach_rate":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":"kept divided by sold, from 0 to 1. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"items":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item in the slot: the slot's own, or a choice it offers. Changing it puts the new item on a new line and drops the old one, while nothing in the slot has been handed over."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of the packages kept the slot with this item in it."}},"required":["item_id","sku","item_name","count"],"additionalProperties":false},"description":"Which items customers had in the slot, most chosen first."}},"required":["package_component_id","component_name","kept","dropped","returned","attach_rate","items"],"additionalProperties":false},"description":"Each slot: how often it was kept, and with which item."}},"required":["package_id","package_code","package_name","sold","delivered","returned","booked_amount","returned_amount","revenue_fulfilled","cost_fulfilled","components"],"additionalProperties":false},"description":"Each package sold in the period, most sold first."}},"required":["date_from","date_to","packages"],"additionalProperties":false,"description":"How many of each package sold in a period, what they were sold for and earned, and how often customers kept each component."},"SalesOrderLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item sold. Fixed once the line exists."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"tracking":{"type":"string","enum":["none","quantity","serial"],"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},"description":{"type":"string","description":"What the line says on the order. Defaults to the item's name."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many the customer is buying, in the item's unit. 0 drops the line. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. A decimal string with up to 2 decimal places, for example \"12.50\"."},"list_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit of sale (a unit, or a pack on a line sold by the pack) lists at for this order, options included: the lowest of the store's regular price and the customer's offer lists on the order date, for the line's quantity (getItemPrices shows how). A price below it, or a discount, is weighed against the key's discount allowance. Moves with the quantity, customer, store and date while nothing of the line has been handed over. NULL when nothing prices the item, and on a package's component, which the package prices. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What one unit of sale (a unit, or a pack on a line sold by the pack) lists at for this order, options included: the lowest of the store's regular price and the customer's offer lists on the order date, for the line's quantity (getItemPrices shows how). A price below it, or a discount, is weighed against the key's discount allowance. Moves with the quantity, customer, store and date while nothing of the line has been handed over. NULL when nothing prices the item, and on a package's component, which the package prices. Set by the system."},"regular_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit of sale would sell for at the store with no offer list: the store's regular list, else the item's or pack's own price, options included. Below it, list_price is what an offer list saves the customer. Moves with list_price. NULL when nothing prices the item, and on a package's component. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What one unit of sale would sell for at the store with no offer list: the store's regular list, else the item's or pack's own price, options included. Below it, list_price is what an offer list saves the customer. Moves with list_price. NULL when nothing prices the item, and on a package's component. Set by the system."},"price_source":{"anyOf":[{"type":"string","enum":["list","item","hand"]},{"type":"null"}],"description":"Where the agreed price (unit_price, or pack_price on a line sold by the pack) came from. list: a price list's price, list_price. item: the item's or pack's own price, list_price. hand: a price someone set, other than list_price; below it, it was weighed as a discount. Null on a package's component, which its package prices."},"price_list_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The price list list_price came from. NULL when it is the item's or pack's own price. Set by the system."},"price_list_code":{"description":"The code of the price list list_price came from. Null for the item's or pack's own price.","type":["string","null"]},"price_reason":{"description":"Why the line costs what it does when a price list priced it (price_source list), as a person reads it, for example \"CONTR: 10% off Water Care, 3 or more\". Null otherwise.","type":["string","null"]},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists.","type":["string","null"]},"pack_size":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit are in one pack, as the pack was when the line was written. Calculated. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many of the item's unit are in one pack, as the pack was when the line was written. Calculated."},"packs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Agreed price of one pack, before discount and tax. Defaults to list_price."},"discount_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. A decimal string with up to 2 decimal places, for example \"12.50\"."},"extended_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the line comes to before tax: quantity times unit_price, or for a line sold by the pack quantity times pack_price over pack_size (packs times pack_price, for whole packs), less discount_amount. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_class_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How the line is taxed. Taken from the item, or the tenant's default class. Calculated."},"tax_class_name":{"type":"string","description":"The name of the tax class the line is taxed as."},"tax_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the line, the sum of its sales_order_line_tax rows. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"item_configuration_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a model built with options: how the customer's unit is built, or is to be built. Chosen on the line, or taken from the serialized unit named, whose own it must be. Carried to a special order bought for the line, so the factory builds what was sold. Fixed once the line is handed over."},"options":{"type":"array","items":{"$ref":"#/components/schemas/ChosenOption"},"description":"The options the customer's unit is built with, group by group. Empty for an item sold as it is."},"options_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the line's options add to the price of one unit, as they were priced when chosen: part of unit_price. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"sales_order_package_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The package on the order this line is a component of, or NULL for a line sold on its own. A component's quantity and price come from the package: change them there. Set by the system."},"quantity_fulfilled":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many have been handed over, less any returned. Calculated. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_to_fulfil":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are still to be handed over: quantity less quantity_fulfilled. A decimal string with up to 3 decimal places, for example \"12.500\"."},"revenue_fulfilled":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of extended_price earned by what has been handed over. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_fulfilled":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of tax_amount due on what has been handed over. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost_fulfilled":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods handed over cost the tenant. Revenue less this is the line's gross profit. Calculated. Null without cost.view."},"goods_coming":{"type":"array","items":{"$ref":"#/components/schemas/GoodsComing"},"description":"On an open order: the goods on purchase orders this line waits on, earliest date first, each with its vendor's date and whether it is late. Empty when the line waits on nothing: handed over, a unit chosen, or covered by what its store holds."},"quantity_not_ordered":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"On an open order: how many of what is still to hand over neither its store's stock nor anything on order covers. Order them (createPurchaseOrder, with sales_order_line_id for a special order). 0 otherwise. A decimal string with up to 3 decimal places, for example \"12.500\"."},"return_window":{"anyOf":[{"type":"object","properties":{"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days after they were handed over the goods come back without order.cancel."},"held":{"type":"array","items":{"type":"object","properties":{"handed_over_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day they were handed over."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many handed over that day are still with the customer. A decimal string with up to 3 decimal places, for example \"12.500\"."},"returnable_until":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day they come back without order.cancel: handed_over_on plus days."},"days_past":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days ago returnable_until passed, at the order's location: 0 while within it."}},"required":["handed_over_on","quantity","returnable_until","days_past"],"additionalProperties":false},"description":"What is still with the customer, by the day it was handed over, newest first. A return takes the newest first."}},"required":["days","held"],"additionalProperties":false},{"type":"null"}],"description":"The window this line's goods come back in: their category's returnable_days, else the business's return_days (getPolicy). Past it a return needs order.cancel where the goods come back. Null when they come back at any time."}},"required":["id","line_number","item_id","sku","item_name","tracking","description","quantity","unit_price","list_price","regular_price","price_source","price_list_id","price_list_code","price_reason","pack_text","pack_size","packs","pack_price","discount_amount","extended_price","tax_class_id","tax_class_name","tax_amount","serialized_unit_id","serial_number","item_configuration_id","options","options_price","sales_order_package_id","quantity_fulfilled","quantity_to_fulfil","revenue_fulfilled","tax_fulfilled","cost_fulfilled","goods_coming","quantity_not_ordered","return_window"],"additionalProperties":false,"description":"One item on a sales order: how many, at what price. Lines can be added and changed while the order is a quote or open; a line is fixed once any of it has been handed over. To drop a line, set its quantity to 0. When goods are returned the system reduces the line by the returned quantity. schema.org: OrderItem."},"SalesCredit":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who gets the credit."},"name":{"type":"string","description":"The staff member's name."},"share":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"Their part of the sale, as a fraction of 1. Set to 0 to take someone off the sale. A decimal string with up to 4 decimal places, for example \"12.5000\"."}},"required":["staff_member_id","name","share"],"additionalProperties":false,"description":"One staff member's credit for one sale. A sale made by one person has one row with share 1; two people who split a deal each have a row, for example 0.6 and 0.4. Commissions and quota attainment follow these shares. The shares of an order must add up to 1 by the time goods are handed over."},"Payment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order paid toward."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order's customer. Taken from the order, and kept the same as the order's when its customer is named or corrected later."},"customer_name":{"description":"The customer's name, or null for an anonymous counter sale.","type":["string","null"]},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How it was paid. This decides which account the money is recorded in."},"payment_method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive for money received, negative for a refund. An order cannot be paid beyond its total, nor refunded beyond the money it holds for the customer: paid less handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the payment was taken. Defaults to the order's location."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took the payment or gave the refund."},"staff_member_name":{"description":"The staff member's name, or null when none took the payment.","type":["string","null"]},"reference":{"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused.","type":["string","null"]},"paid_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the payment, at the location. Calculated."},"note":{"description":"Anything worth recording, for example why a refund was given.","type":["string","null"]},"reverses_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Set when this row undoes an earlier payment on the same order that turned out not to be money: a check that bounced, a card payment charged back. The amount is negative and no more than is left of that payment, and it goes back by the same payment method. Unlike a refund the business chooses to give, a reversal is a fact to be recorded, so the customer's credit limit does not stop it: they simply owe again. For a card processor's method only, it can also undo a refund the processor failed to pay out, or a chargeback the business won: then the amount is positive. NULL for an ordinary payment or refund."},"financing_application_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a payment by a lender's method: the approved application it is taken against. Left out, the order's one approved application with that lender is used (for a refund, the one holding financed money). A reversal takes the one of the payment it reverses. NULL for every other method."},"cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer the cash went into or came out of, for a payment by a cash method. Needed for cash when the business keeps every dollar in a drawer (tenant_policy.cash_needs_drawer), except on a reversal. A reversal of cash taken into a drawer still open comes out of that drawer, named or not; once that drawer is closed, out of its safe (NULL here). NULL for any other method. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The drawer's number, or null when none was named. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"gift_card_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The gift card a payment by the Gift card method spent, or a refund put money back onto. Set exactly for that method. A card pays only in the request that presented its code; a refund goes onto a card that paid the order, up to what it paid there, or onto one presented or made in the same request."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number the business quotes for the card: on its receipts, its records and the phone. Calculated. It cannot spend the card, which takes the code. Null for any other method."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","sales_order_id","order_number","customer_id","customer_name","payment_method_id","payment_method_name","amount","location_id","location_name","staff_member_id","staff_member_name","reference","paid_on","note","reverses_payment_id","financing_application_id","cash_drawer_session_id","cash_drawer_session_number","gift_card_id","gift_card_number","created_at"],"additionalProperties":false,"description":"Money a customer paid toward one order, or was refunded from it. Rows are only ever added; a refund is a new row with a negative amount. Money paid before the goods are handed over is a deposit. A payment that never arrived (a bounced check, a card chargeback) is undone by a row that names it in reverses_payment_id."},"SalesFulfilment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The line being handed over or returned."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many: positive when handed to the customer, negative when returned. A line cannot be handed over beyond its quantity, or returned beyond what was handed over. A decimal string with up to 3 decimal places, for example \"12.500\"."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock leaves from or comes back to. Defaults to where the line's serialized unit is, else the order's location. Goods the customer carries out (an order with no delivery site) are handed over only at a location in the order's tax area. Income, tax and cost of goods count for the order's location whatever this is; only the stock is this location's."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"lots":{"type":"array","items":{"$ref":"#/components/schemas/MovedLot"},"description":"For an item tracked by lot: each lot handed over (or, on a return, come back), and how many. Empty otherwise."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who handed the goods over or took them back."},"staff_member_name":{"description":"The staff member's name, or null when none handed the goods over.","type":["string","null"]},"fulfilled_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it happened, at the store that made the sale (the order's location), wherever the goods left from: that store's day carries it. Sales reports, the day close, the journal and quota periods use this date. Calculated."},"revenue_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Revenue earned by this hand-over, before tax: the line's price in proportion to the quantity. Negative for a return. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax that fell due on this hand-over. Negative for a return. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"restocking_fee":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"On a return: what the business kept of what the goods sold for, by its restocking fee (tenant_policy.restocking_fee_percent, or the item's category's). Added to the order's fee_total and posted as fee income. 0 on a hand-over. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"restocking_fee_waived":{"type":"boolean","description":"True on a return the business chose to take back without its restocking fee, for example faulty goods. Waiving it needs order.discount where the goods come back."},"restock":{"type":"boolean","description":"On a return: true (the default) when the goods go back into stock to sell again, at what the hand-overs they come off cost. False when they cannot be sold again (opened, used, damaged): nothing comes into stock and the cost of the sale stands, as for a return without a receipt. Never false for a serial-tracked unit, which is written down on its own. Always true on a hand-over."},"restocking_fee_tax":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The sales tax on the restocking fee, when the business taxes its fees (tenant_policy.fees_taxed): at the order's tax area, at the rates in force on the day of the return, for the class of the goods. Added to the order's fee_tax_total. 0 otherwise. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"note":{"description":"Anything worth recording, for example why goods were returned.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","sales_order_line_id","line_number","sku","item_name","quantity","location_id","location_name","serial_number","lots","staff_member_id","staff_member_name","fulfilled_on","revenue_amount","tax_amount","restocking_fee","restocking_fee_waived","restock","restocking_fee_tax","note","created_at"],"additionalProperties":false,"description":"Goods or services on one order line handed to the customer, or returned by them. This is the moment a sale counts: revenue, tax, cost and commission all date from it. Rows are only ever added; a return is a new row with a negative quantity."},"SalesOrderSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"description":"The customer's name, or null for an anonymous counter sale.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location making the sale. Stock is handed over from here unless a fulfilment says otherwise."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the goods are to be delivered: one of the customer's own sites. NULL when the customer takes them at the location."},"site_name":{"description":"The delivery site's name, or null when there is none.","type":["string","null"]},"order_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over."},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a quote: the last date the offered prices hold, and the last date it holds any serialized unit its lines name (when the business's quotes hold units, tenant_policy.quote_holds_units). After it, the units are free to sell to anyone; a new expires_on holds them again if no one has taken them. A new quote written up without one gets the order date plus the business's quote_valid_days, when it has set them."},"opened_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the customer agreed: the day a quote was opened, or the order date of an order written up open. A booked sale dates from here, a delivered one from its hand-overs. NULL while it is a quote, and for a quote cancelled unopened. Set by the system."},"cancelled_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the order was cancelled, at its location. A cancellation fee kept is owed from it if the payment it was kept from fails. NULL unless the order is cancelled. Set by the system."},"is_expired":{"type":"boolean","description":"True for a quote whose expires_on has passed: it cannot be opened until expires_on is moved."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the sale is taxed in. Defaults to the delivery site's area, or the location's when there is no site or the site has none. NULL means no tax is charged."},"tax_area_name":{"description":"The tax area's name, or null when no tax is charged.","type":["string","null"]},"is_tax_exempt":{"type":"boolean","description":"True when no tax is charged on this order. Starts true for a tax-exempt customer when one of their certificates covers the order's date and state, or when none does and the business sells to them untaxed anyway (tenant_policy.exemption_without_certificate). Setting it true without a certificate that covers it needs tax.override."},"tax_exemption_status":{"type":"string","enum":["taxed","certificate","without_certificate","certificate_lapsed"],"description":"taxed: taxed, and the customer does not buy tax-exempt. certificate: untaxed on the customer's certificate (tax_exemption_certificate_id). without_certificate: untaxed with no certificate covering it, exempted by someone holding tax.override or by the business's policy (exemption_without_certificate). certificate_lapsed: the customer buys tax-exempt, but no certificate of theirs covers this sale's date and state, so it is taxed (tax_exemption_note says why): record their current certificate (recordTaxExemptionCertificate), or have someone holding tax.override exempt it."},"tax_exemption_note":{"description":"With certificate_lapsed, and with without_certificate for a customer who buys tax-exempt: why no certificate of theirs covers the sale, in plain words. Null otherwise.","type":["string","null"]},"tax_exemption_certificate_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's certificate that covers this untaxed sale: in force on its order date and good in the state it is taxed in. NULL on a taxed sale, and on one untaxed without a certificate (exempted by someone holding tax.override, or by the business's policy). Set by the system, and fixed with the tax once goods are handed over or money taken."},"tax_exemption_certificate_number":{"description":"That certificate's number, as an auditor would look it up, or null when none covers the sale.","type":["string","null"]},"tax_rounding":{"type":"string","enum":["line","order"],"description":"How the order's tax is rounded to the cent, for each jurisdiction: line, each line on its own; order, once on the whole order and shared out to the lines. Taken from the tenant's policy when the order is written up, and kept, so a change of policy never changes an order already agreed. Set by the system."},"tax_rate_date":{"type":"string","enum":["order_date","hand_over"],"description":"Which day's tax rates the order is charged at: order_date, the rates in force on order_date; hand_over, the rates in force when the goods are handed over (today's, until then). Taken from the tenant's policy when the order is written up, and kept. Set by the system."},"sales_channel_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The channel the order came through. NULL when not recorded."},"sales_channel_name":{"description":"The channel's name, when the order names one.","type":["string","null"]},"lead_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's lead this quote or order came from, named by whoever writes it up: never assumed, so a customer with a spa lead open who buys chemicals has not bought the spa. Agreeing to an order linked to a lead (opening it, or linking it once open) wins the lead. Fixed once the order is agreed on it."},"lead_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The lead's number, or null when it is linked to none."},"customer_open_lead":{"anyOf":[{"type":"object","properties":{"lead_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lead."},"lead_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the business and assigned automatically."},"stage":{"type":"string","enum":["new","contacted","visited","quoted"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages."}},"required":["lead_id","lead_number","stage"],"additionalProperties":false},{"type":"null"}],"description":"The customer's open lead this order is not linked to, or null. Link it (updateSalesOrder, lead_id) only when this is what they asked about: a lead is won when an order linked to it is agreed."},"subscription_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The subscription this order is a cycle of. Set by the system. NULL for an ordinary order."},"subscription_name":{"description":"The subscription's name, or null when the order is not one of its cycles.","type":["string","null"]},"cycle_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The due date of the subscription cycle this order is for. Set by the system. NULL for an ordinary order."},"subtotal":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sum of the lines before tax, after discounts. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the order. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fulfilled_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of total, tax included, for goods and services already handed over, plus the fees kept and any tax on them. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer has paid so far, less refunds. Calculated. Paid beyond fulfilled is a deposit; fulfilled beyond paid is owed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fee_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Fees the business kept on the order, by its policy: restocking fees on goods that came back, and the cancellation fee. Part of total and fulfilled_total, and posted as fee income. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancellation_fee":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the business kept of the money paid when the order was cancelled, by its cancellation fee policy, before tax. 0 unless the order was cancelled with money kept. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancellation_fee_tax":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The sales tax on the cancellation fee, when the business taxes its fees (tenant_policy.fees_taxed): kept out of what was paid with the fee. Part of fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fee_tax_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The sales tax on the fees kept (fee_total), when the business taxes its fees: the cancellation fee's and each return's restocking fee's. Part of total and fulfilled_total, and owed to the tax jurisdictions. 0 when fees are not taxed. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancellation_fee_terms":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"On an open order: the most the business would keep of what is paid if it were cancelled now, by its cancellation fee policy, with the tax on it when the business taxes its fees. 0 when it would keep nothing, and on any other order. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is left to pay on the whole order: total less paid_total, or 0. On a cancelled order, only what it still owes (amount_owed). Money held beyond the total is in deposit_held. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credit_available":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"How much more the customer may owe, across all their orders, before goods stop being handed over: their credit limit less what they owe now. Null when they have no limit or the order has no customer."},"amount_owed":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer owes now for goods already handed over: fulfilled_total less paid_total, or 0. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_held":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money held for the customer: paid_total less fulfilled_total, or 0, and the most refundPayment can give back. Of it, owed_back is owed back to the customer; only the rest is a deposit, paid ahead for goods not yet handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_back":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money owed back to the customer for goods returned or called off: paid_total beyond what the order now comes to (total, or on a cancelled order fulfilled_total, which counts the fee kept), or 0. Give it back with refundPayment. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_back_in_money":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of owed_back a refund by money (refundPayment, any method but the trade-in) can give back. The rest, owed_back_by_trade_in, goes back only by giving a trade-in's unit back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_back_by_trade_in":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of owed_back that trade-ins paid whose units are still in stock as they came in (trade_in_held): it goes back by giving the unit back (giveBackTradeIn), which refunds its allowance, not as money. A decimal string with up to 2 decimal places, for example \"12.50\"."},"trade_in_pending":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer is allowed for old units traded in toward the order and not yet received (trade_ins, status agreed): part of balance_due that the units pay when they are received, not money to take. A decimal string with up to 2 decimal places, for example \"12.50\"."},"money_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is left to pay in money: balance_due less trade_in_pending, or 0. While a trade is agreed, takePayment takes no more than this, leaving room for the allowance the unit pays when it is received. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_required":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The deposit the business asks before an order opens (getPolicy, min_deposit_percent): that share of the total less the allowances of trade-ins agreed or received. 0 on an order made by a subscription, work order or sales channel, and once it is no longer a quote or open. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_short":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What more must be paid in money to reach deposit_required, not counting what received trade-ins paid, or 0. A quote opens only with at least this much as its deposit, and while an open order is short, a change that raises its total is refused, unless someone holding credit.manage does it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"trade_in_held":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What received trade-ins paid toward the order whose units are still in stock as they came in: only giving a unit back (giveBackTradeIn) returns it, so refundPayment gives back at most paid_total less this. Once the unit is sold, disposed of or written down, its allowance can go back as money. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"description":"Free-form notes for staff.","type":["string","null"]},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","order_number","status","customer_id","customer_name","location_id","location_name","site_id","site_name","order_date","expires_on","opened_on","cancelled_on","is_expired","tax_area_id","tax_area_name","is_tax_exempt","tax_exemption_status","tax_exemption_note","tax_exemption_certificate_id","tax_exemption_certificate_number","tax_rounding","tax_rate_date","sales_channel_id","sales_channel_name","lead_id","lead_number","customer_open_lead","subscription_id","subscription_name","cycle_due_on","subtotal","tax_total","total","fulfilled_total","paid_total","fee_total","cancellation_fee","cancellation_fee_tax","fee_tax_total","cancellation_fee_terms","balance_due","credit_available","amount_owed","deposit_held","owed_back","owed_back_in_money","owed_back_by_trade_in","trade_in_pending","money_due","deposit_required","deposit_short","trade_in_held","notes","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order. As listed: getSalesOrder adds lines, credit, payments and hand-overs."},"SalesOrder":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"description":"The customer's name, or null for an anonymous counter sale.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location making the sale. Stock is handed over from here unless a fulfilment says otherwise."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the goods are to be delivered: one of the customer's own sites. NULL when the customer takes them at the location."},"site_name":{"description":"The delivery site's name, or null when there is none.","type":["string","null"]},"order_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over."},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a quote: the last date the offered prices hold, and the last date it holds any serialized unit its lines name (when the business's quotes hold units, tenant_policy.quote_holds_units). After it, the units are free to sell to anyone; a new expires_on holds them again if no one has taken them. A new quote written up without one gets the order date plus the business's quote_valid_days, when it has set them."},"opened_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the customer agreed: the day a quote was opened, or the order date of an order written up open. A booked sale dates from here, a delivered one from its hand-overs. NULL while it is a quote, and for a quote cancelled unopened. Set by the system."},"cancelled_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the order was cancelled, at its location. A cancellation fee kept is owed from it if the payment it was kept from fails. NULL unless the order is cancelled. Set by the system."},"is_expired":{"type":"boolean","description":"True for a quote whose expires_on has passed: it cannot be opened until expires_on is moved."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the sale is taxed in. Defaults to the delivery site's area, or the location's when there is no site or the site has none. NULL means no tax is charged."},"tax_area_name":{"description":"The tax area's name, or null when no tax is charged.","type":["string","null"]},"is_tax_exempt":{"type":"boolean","description":"True when no tax is charged on this order. Starts true for a tax-exempt customer when one of their certificates covers the order's date and state, or when none does and the business sells to them untaxed anyway (tenant_policy.exemption_without_certificate). Setting it true without a certificate that covers it needs tax.override."},"tax_exemption_status":{"type":"string","enum":["taxed","certificate","without_certificate","certificate_lapsed"],"description":"taxed: taxed, and the customer does not buy tax-exempt. certificate: untaxed on the customer's certificate (tax_exemption_certificate_id). without_certificate: untaxed with no certificate covering it, exempted by someone holding tax.override or by the business's policy (exemption_without_certificate). certificate_lapsed: the customer buys tax-exempt, but no certificate of theirs covers this sale's date and state, so it is taxed (tax_exemption_note says why): record their current certificate (recordTaxExemptionCertificate), or have someone holding tax.override exempt it."},"tax_exemption_note":{"description":"With certificate_lapsed, and with without_certificate for a customer who buys tax-exempt: why no certificate of theirs covers the sale, in plain words. Null otherwise.","type":["string","null"]},"tax_exemption_certificate_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's certificate that covers this untaxed sale: in force on its order date and good in the state it is taxed in. NULL on a taxed sale, and on one untaxed without a certificate (exempted by someone holding tax.override, or by the business's policy). Set by the system, and fixed with the tax once goods are handed over or money taken."},"tax_exemption_certificate_number":{"description":"That certificate's number, as an auditor would look it up, or null when none covers the sale.","type":["string","null"]},"tax_rounding":{"type":"string","enum":["line","order"],"description":"How the order's tax is rounded to the cent, for each jurisdiction: line, each line on its own; order, once on the whole order and shared out to the lines. Taken from the tenant's policy when the order is written up, and kept, so a change of policy never changes an order already agreed. Set by the system."},"tax_rate_date":{"type":"string","enum":["order_date","hand_over"],"description":"Which day's tax rates the order is charged at: order_date, the rates in force on order_date; hand_over, the rates in force when the goods are handed over (today's, until then). Taken from the tenant's policy when the order is written up, and kept. Set by the system."},"sales_channel_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The channel the order came through. NULL when not recorded."},"sales_channel_name":{"description":"The channel's name, when the order names one.","type":["string","null"]},"lead_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's lead this quote or order came from, named by whoever writes it up: never assumed, so a customer with a spa lead open who buys chemicals has not bought the spa. Agreeing to an order linked to a lead (opening it, or linking it once open) wins the lead. Fixed once the order is agreed on it."},"lead_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The lead's number, or null when it is linked to none."},"customer_open_lead":{"anyOf":[{"type":"object","properties":{"lead_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lead."},"lead_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the business and assigned automatically."},"stage":{"type":"string","enum":["new","contacted","visited","quoted"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages."}},"required":["lead_id","lead_number","stage"],"additionalProperties":false},{"type":"null"}],"description":"The customer's open lead this order is not linked to, or null. Link it (updateSalesOrder, lead_id) only when this is what they asked about: a lead is won when an order linked to it is agreed."},"subscription_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The subscription this order is a cycle of. Set by the system. NULL for an ordinary order."},"subscription_name":{"description":"The subscription's name, or null when the order is not one of its cycles.","type":["string","null"]},"cycle_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The due date of the subscription cycle this order is for. Set by the system. NULL for an ordinary order."},"subtotal":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sum of the lines before tax, after discounts. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the order. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fulfilled_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of total, tax included, for goods and services already handed over, plus the fees kept and any tax on them. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer has paid so far, less refunds. Calculated. Paid beyond fulfilled is a deposit; fulfilled beyond paid is owed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fee_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Fees the business kept on the order, by its policy: restocking fees on goods that came back, and the cancellation fee. Part of total and fulfilled_total, and posted as fee income. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancellation_fee":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the business kept of the money paid when the order was cancelled, by its cancellation fee policy, before tax. 0 unless the order was cancelled with money kept. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancellation_fee_tax":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The sales tax on the cancellation fee, when the business taxes its fees (tenant_policy.fees_taxed): kept out of what was paid with the fee. Part of fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fee_tax_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The sales tax on the fees kept (fee_total), when the business taxes its fees: the cancellation fee's and each return's restocking fee's. Part of total and fulfilled_total, and owed to the tax jurisdictions. 0 when fees are not taxed. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancellation_fee_terms":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"On an open order: the most the business would keep of what is paid if it were cancelled now, by its cancellation fee policy, with the tax on it when the business taxes its fees. 0 when it would keep nothing, and on any other order. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is left to pay on the whole order: total less paid_total, or 0. On a cancelled order, only what it still owes (amount_owed). Money held beyond the total is in deposit_held. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credit_available":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"How much more the customer may owe, across all their orders, before goods stop being handed over: their credit limit less what they owe now. Null when they have no limit or the order has no customer."},"amount_owed":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer owes now for goods already handed over: fulfilled_total less paid_total, or 0. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_held":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money held for the customer: paid_total less fulfilled_total, or 0, and the most refundPayment can give back. Of it, owed_back is owed back to the customer; only the rest is a deposit, paid ahead for goods not yet handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_back":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money owed back to the customer for goods returned or called off: paid_total beyond what the order now comes to (total, or on a cancelled order fulfilled_total, which counts the fee kept), or 0. Give it back with refundPayment. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_back_in_money":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of owed_back a refund by money (refundPayment, any method but the trade-in) can give back. The rest, owed_back_by_trade_in, goes back only by giving a trade-in's unit back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_back_by_trade_in":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of owed_back that trade-ins paid whose units are still in stock as they came in (trade_in_held): it goes back by giving the unit back (giveBackTradeIn), which refunds its allowance, not as money. A decimal string with up to 2 decimal places, for example \"12.50\"."},"trade_in_pending":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer is allowed for old units traded in toward the order and not yet received (trade_ins, status agreed): part of balance_due that the units pay when they are received, not money to take. A decimal string with up to 2 decimal places, for example \"12.50\"."},"money_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is left to pay in money: balance_due less trade_in_pending, or 0. While a trade is agreed, takePayment takes no more than this, leaving room for the allowance the unit pays when it is received. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_required":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The deposit the business asks before an order opens (getPolicy, min_deposit_percent): that share of the total less the allowances of trade-ins agreed or received. 0 on an order made by a subscription, work order or sales channel, and once it is no longer a quote or open. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_short":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What more must be paid in money to reach deposit_required, not counting what received trade-ins paid, or 0. A quote opens only with at least this much as its deposit, and while an open order is short, a change that raises its total is refused, unless someone holding credit.manage does it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"trade_in_held":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What received trade-ins paid toward the order whose units are still in stock as they came in: only giving a unit back (giveBackTradeIn) returns it, so refundPayment gives back at most paid_total less this. Once the unit is sold, disposed of or written down, its allowance can go back as money. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"description":"Free-form notes for staff.","type":["string","null"]},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/SalesOrderLine"},"description":"The lines, in line_number order. Dropped lines have quantity 0."},"packages":{"type":"array","items":{"$ref":"#/components/schemas/SalesOrderPackage"},"description":"The packages sold on the order, each priced as a whole and shared among its lines."},"credits":{"type":"array","items":{"$ref":"#/components/schemas/SalesCredit"},"description":"Who gets credit for the sale, and their shares."},"payments":{"type":"array","items":{"$ref":"#/components/schemas/Payment"},"description":"Every payment, refund and reversal, oldest first."},"financing_applications":{"type":"array","items":{"$ref":"#/components/schemas/FinancingApplication"},"description":"The customer's applications to lenders to finance this order, oldest first, each with its next steps."},"fulfilments":{"type":"array","items":{"$ref":"#/components/schemas/SalesFulfilment"},"description":"Every hand-over and return, oldest first."},"trade_ins":{"type":"array","items":{"$ref":"#/components/schemas/TradeIn"},"description":"Old units the customer traded in toward the order, oldest first, each with its next steps."},"financing_approved":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What lenders pay toward the order: financed through its approved applications so far, plus what they can still take (capped at money_due) while the order is a quote or open. A decimal string with up to 2 decimal places, for example \"12.50\"."},"financing_pending":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer asked lenders for in applications still waiting on an answer (status applied): not counted anywhere else until approved. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_by_customer":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer paid themselves: paid_total less what received trade-ins and lenders paid. Negative when more was given back than they paid. A decimal string with up to 2 decimal places, for example \"12.50\"."},"customer_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is left for the customer to pay themselves: money_due less what approved financing can still take, or 0. Take this from the customer, and the rest from the lender (takePayment with financing_application_id). A decimal string with up to 2 decimal places, for example \"12.50\"."},"repriced":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"price_before":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What was agreed for one unit of sale (a unit, or a pack) before. A decimal string with up to 2 decimal places, for example \"12.50\"."},"price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is agreed for it now. A decimal string with up to 2 decimal places, for example \"12.50\"."},"list_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit of sale (a unit, or a pack on a line sold by the pack) lists at for this order, options included: the lowest of the store's regular price and the customer's offer lists on the order date, for the line's quantity (getItemPrices shows how). A price below it, or a discount, is weighed against the key's discount allowance. Moves with the quantity, customer, store and date while nothing of the line has been handed over. NULL when nothing prices the item, and on a package's component, which the package prices. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What one unit of sale (a unit, or a pack on a line sold by the pack) lists at for this order, options included: the lowest of the store's regular price and the customer's offer lists on the order date, for the line's quantity (getItemPrices shows how). A price below it, or a discount, is weighed against the key's discount allowance. Moves with the quantity, customer, store and date while nothing of the line has been handed over. NULL when nothing prices the item, and on a package's component, which the package prices. Set by the system."}},"required":["sales_order_line_id","line_number","sku","price_before","price","list_price"],"additionalProperties":false}},{"type":"null"}],"description":"From updateSalesOrder and repriceSalesOrder: the lines whose agreed price the change moved (a new customer, store or date prices lines at their list price again), empty when none moved. Null from every other operation."},"prices_current":{"type":"boolean","description":"False when a line nothing of which is handed over lists at other than the price lists and items give it now (a price or a list changed since it was written): repriceSalesOrder brings the order up to date. True otherwise."},"stock_below_zero":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"Its SKU."},"item_name":{"type":"string","description":"Its name."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where its stock is below zero: a location this order handed it over from."},"location_name":{"type":"string","description":"That location's name."},"branch_code":{"type":"string","description":"Its branch code."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"What the system shows on hand there now: less than 0. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","sku","item_name","location_id","location_name","branch_code","quantity_on_hand"],"additionalProperties":false},"description":"Items this order handed over from a location where the system now shows less than zero on hand: more was sold than it knew of, because receiving or a count is behind (the business allows it: allow_negative_stock). The sale stands; tell the person, so the stock is received or counted. Empty when none."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","order_number","status","customer_id","customer_name","location_id","location_name","site_id","site_name","order_date","expires_on","opened_on","cancelled_on","is_expired","tax_area_id","tax_area_name","is_tax_exempt","tax_exemption_status","tax_exemption_note","tax_exemption_certificate_id","tax_exemption_certificate_number","tax_rounding","tax_rate_date","sales_channel_id","sales_channel_name","lead_id","lead_number","customer_open_lead","subscription_id","subscription_name","cycle_due_on","subtotal","tax_total","total","fulfilled_total","paid_total","fee_total","cancellation_fee","cancellation_fee_tax","fee_tax_total","cancellation_fee_terms","balance_due","credit_available","amount_owed","deposit_held","owed_back","owed_back_in_money","owed_back_by_trade_in","trade_in_pending","money_due","deposit_required","deposit_short","trade_in_held","notes","custom_fields","created_at","updated_at","lines","packages","credits","payments","financing_applications","fulfilments","trade_ins","financing_approved","financing_pending","paid_by_customer","customer_due","repriced","prices_current","stock_below_zero","next"],"additionalProperties":false,"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order."},"SalesOrderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SalesOrderSummary"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of sales orders and the cursor for the next."},"PaymentPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Payment"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of payments and the cursor for the next."},"CounterSalePrice":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location making the sale. Stock is handed over from here unless a fulfilment says otherwise."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"description":"The customer's name, or null for an anonymous sale.","type":["string","null"]},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the sale is taxed in. Defaults to the delivery site's area, or the location's when there is no site or the site has none. NULL means no tax is charged."},"tax_area_name":{"description":"The tax area's name, or null when no tax is charged.","type":["string","null"]},"is_tax_exempt":{"type":"boolean","description":"True when no tax is charged on this order. Starts true for a tax-exempt customer when one of their certificates covers the order's date and state, or when none does and the business sells to them untaxed anyway (tenant_policy.exemption_without_certificate). Setting it true without a certificate that covers it needs tax.override."},"tax_exemption_status":{"type":"string","enum":["taxed","certificate","without_certificate","certificate_lapsed"],"description":"taxed: taxed, and the customer does not buy tax-exempt. certificate: untaxed on the customer's certificate (tax_exemption_certificate_id). without_certificate: untaxed with no certificate covering it, exempted by someone holding tax.override or by the business's policy (exemption_without_certificate). certificate_lapsed: the customer buys tax-exempt, but no certificate of theirs covers this sale's date and state, so it is taxed (tax_exemption_note says why): record their current certificate (recordTaxExemptionCertificate), or have someone holding tax.override exempt it."},"tax_exemption_note":{"description":"With certificate_lapsed, and with without_certificate for a customer who buys tax-exempt: why no certificate of theirs covers the sale, in plain words. Null otherwise.","type":["string","null"]},"tax_exemption_certificate_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's certificate that covers this untaxed sale: in force on its order date and good in the state it is taxed in. NULL on a taxed sale, and on one untaxed without a certificate (exempted by someone holding tax.override, or by the business's policy). Set by the system, and fixed with the tax once goods are handed over or money taken."},"tax_exemption_certificate_number":{"description":"That certificate's number, as an auditor would look it up, or null when none covers the sale.","type":["string","null"]},"lines":{"type":"array","items":{"type":"object","properties":{"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item sold. Fixed once the line exists."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"tracking":{"type":"string","enum":["none","quantity","serial"],"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},"description":{"type":"string","description":"What the line says on the order. Defaults to the item's name."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many the customer is buying, in the item's unit. 0 drops the line. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. A decimal string with up to 2 decimal places, for example \"12.50\"."},"list_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit of sale (a unit, or a pack on a line sold by the pack) lists at for this order, options included: the lowest of the store's regular price and the customer's offer lists on the order date, for the line's quantity (getItemPrices shows how). A price below it, or a discount, is weighed against the key's discount allowance. Moves with the quantity, customer, store and date while nothing of the line has been handed over. NULL when nothing prices the item, and on a package's component, which the package prices. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What one unit of sale (a unit, or a pack on a line sold by the pack) lists at for this order, options included: the lowest of the store's regular price and the customer's offer lists on the order date, for the line's quantity (getItemPrices shows how). A price below it, or a discount, is weighed against the key's discount allowance. Moves with the quantity, customer, store and date while nothing of the line has been handed over. NULL when nothing prices the item, and on a package's component, which the package prices. Set by the system."},"regular_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit of sale would sell for at the store with no offer list: the store's regular list, else the item's or pack's own price, options included. Below it, list_price is what an offer list saves the customer. Moves with list_price. NULL when nothing prices the item, and on a package's component. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What one unit of sale would sell for at the store with no offer list: the store's regular list, else the item's or pack's own price, options included. Below it, list_price is what an offer list saves the customer. Moves with list_price. NULL when nothing prices the item, and on a package's component. Set by the system."},"price_source":{"anyOf":[{"type":"string","enum":["list","item","hand"]},{"type":"null"}],"description":"Where the agreed price (unit_price, or pack_price on a line sold by the pack) came from. list: a price list's price, list_price. item: the item's or pack's own price, list_price. hand: a price someone set, other than list_price; below it, it was weighed as a discount. Null on a package's component, which its package prices."},"price_list_code":{"description":"The code of the price list list_price came from. Null for the item's or pack's own price.","type":["string","null"]},"price_reason":{"description":"Why the line costs what it does when a price list priced it (price_source list), as a person reads it, for example \"CONTR: 10% off Water Care, 3 or more\". Null otherwise.","type":["string","null"]},"pack_text":{"description":"For a line sold by the pack: the pack, as the item's pack is called (case, bag). NULL for a line sold in the item's own unit. Fixed once the line exists.","type":["string","null"]},"packs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many packs: quantity is packs times pack_size. Send it instead of quantity to sell by the pack. NULL on a line sold in units, and once a return leaves part of a pack on the line."},"pack_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price of one pack, before discount and tax. Defaults to list_price. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Agreed price of one pack, before discount and tax. Defaults to list_price."},"discount_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money taken off the whole line, not per unit. At most what the line comes to before it. On a package's line (sales_order_package_id set) it is set by the system: the line's share of what the package saves on its items at their regular prices, together with any discount given on the package. A decimal string with up to 2 decimal places, for example \"12.50\"."},"extended_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the line comes to before tax: quantity times unit_price, or for a line sold by the pack quantity times pack_price over pack_size (packs times pack_price, for whole packs), less discount_amount. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_class_name":{"type":"string","description":"The name of the tax class the line is taxed as."},"tax_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the line, the sum of its sales_order_line_tax rows. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a serial-tracked item: the individual unit the customer is getting. May be chosen later, but must be set before it is handed over. Naming it promises the unit to this customer, so no one else can buy it: on an open order until it is handed over, on a quote until the quote's expires_on has passed, or until it is opened or cancelled if it has none (to hold a unit for a customer until Saturday, quote it with expires_on that Saturday). A unit bought on a special order is named here by the system when it arrives."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"options":{"type":"array","items":{"$ref":"#/components/schemas/ChosenOption"},"description":"The options the customer's unit is built with, group by group. Empty for an item sold as it is."},"options_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the line's options add to the price of one unit, as they were priced when chosen: part of unit_price. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"needs_unit":{"type":"boolean","description":"True for an item tracked by serial number with no serialized_unit_id sent: the sale is refused until the line names the unit (listSerializedUnits, available=true)."}},"required":["line_number","item_id","sku","item_name","tracking","description","quantity","unit_price","list_price","regular_price","price_source","price_list_code","price_reason","pack_text","packs","pack_price","discount_amount","extended_price","tax_class_name","tax_amount","serialized_unit_id","serial_number","options","options_price","needs_unit"],"additionalProperties":false},"description":"The lines as the sale would write them, in the order sent: line_number 1 is the first sent."},"subtotal":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sum of the lines before tax, after discounts. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the order. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the payments of recordCounterSale must add up to, tax included. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["location_id","location_name","customer_id","customer_name","tax_area_id","tax_area_name","is_tax_exempt","tax_exemption_status","tax_exemption_note","tax_exemption_certificate_id","tax_exemption_certificate_number","lines","subtotal","tax_total","total"],"additionalProperties":false,"description":"What a counter sale of these lines would come to, priced and taxed as recordCounterSale would price it now. Nothing is written: no order, no number, no stock held."},"QuoteDocument":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The quote or order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"is_quote":{"type":"boolean","description":"True while it is a quote; false once the customer agreed (an order)."},"format":{"type":"string","enum":["text","html"],"description":"How rendered is written."},"rendered":{"type":"string","description":"The quote (or, once opened, the order) as the customer reads it: what they are getting, packages with what is in them, units by serial number, trade-ins, totals, what is paid, and on a quote the date its prices hold through and the deposit to order. html is a full printable page; text is for a message. Built from the order as it is now, never stored."}},"required":["sales_order_id","order_number","is_quote","format","rendered"],"additionalProperties":false,"description":"A quote or order written out for the customer."},"QuoteEmail":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The quote or order emailed."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"message_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The email, as a message (listMessages)."},"contact_point_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The address it went to."},"to":{"type":"string","description":"That address."},"message_status":{"type":"string","enum":["queued","sending","sent","failed","suppressed"],"description":"queued: waiting for send_after. sending: handed to the email or text service, its answer not yet recorded; set and cleared only by the system. sent: accepted by the provider. failed: the provider refused it, or never confirmed it; status_reason says why. suppressed: not sent, because consent does not allow it, the address is dead, or it was withdrawn (held back by hand, or its visit moved or was called off while it waited); set by the system when the message is queued or taken to send, and by an outside sender if marking it sent is refused because consent was withdrawn meanwhile. Sent, failed and suppressed are final."},"message_status_reason":{"description":"Why the message failed or was suppressed.","type":["string","null"]}},"required":["sales_order_id","order_number","message_id","contact_point_id","to","message_status","message_status_reason"],"additionalProperties":false,"description":"A quote or order emailed to its customer as a service message: it goes only where their consent allows, and one held back says so (suppressed)."},"CardReader":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What staff call it, for example \"Front counter\" or \"Delivery truck 2\"."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store the reader belongs to, where what it takes is counted. A delivery crew's reader belongs to the crew's home store."},"location_name":{"type":"string","description":"The store's name."},"external_id":{"type":"string","description":"The reader's id at the processor (for Stripe, tmr_...)."},"is_active":{"type":"boolean","description":"False once the reader is no longer used: no new charges are sent to it."},"carried_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The technician or delivery driver who carries this reader on the road (a cellular or hotspot reader), so their screens at a customer's door offer it. NULL for a reader that stays at the store's counter."},"carried_by_staff_member_name":{"description":"Who carries it on the road, or null for a counter's reader.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","location_id","location_name","external_id","is_active","carried_by_staff_member_id","carried_by_staff_member_name","created_at","updated_at"],"additionalProperties":false,"description":"A card reader registered to the processor account: a countertop reader at a showroom, or a cellular one a delivery crew carries. Staff send a charge to it and the customer taps, dips or swipes there."},"CardReaderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CardReader"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"A page of card readers."},"CardRefund":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much goes back to the card. Never more than the charge took less what has gone back to it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"status":{"type":"string","enum":["pending","succeeded","failed"],"description":"pending: recorded, the processor not yet done. succeeded: the processor gave the money back. failed: it could not (a closed card, say), so the refund was undone and the order holds the money again."},"origin":{"type":"string","enum":["retavon","outside"],"description":"retavon: given here, through the API. outside: given in the processor's own dashboard, and recorded here when the processor reported it."},"failure_reason":{"description":"Why the processor could not pay it. NULL unless it failed.","type":["string","null"]},"note":{"description":"Why the money was given back.","type":["string","null"]},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Set by the system as the refund is recorded: the refund on the order, a negative payment by the processor's method, or, for one given outside that the order could not refund (it would have left the customer owing), a reversal of the charge's payment."},"external_id":{"description":"The refund's id at the processor (for Stripe, re_...). NULL until the processor makes it.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who gave the refund. NULL for one given outside."},"staff_member_name":{"description":"Who gave the refund, or null for one given outside.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","amount","status","origin","failure_reason","note","payment_id","external_id","staff_member_id","staff_member_name","created_at"],"additionalProperties":false,"description":"Money given back to the card a charge took, through the processor. Recorded as a refund on the order, under the order's lock, before the processor is asked to pay it; a refund the processor fails to pay is undone. A refund given in the processor's own dashboard is recorded when the processor reports it. Written only by the database."},"CardDispute":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much the cardholder disputes. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reason":{"description":"The reason the bank gave, in the processor's words, for example fraudulent or product_not_received.","type":["string","null"]},"status":{"type":"string","enum":["warning_needs_response","warning_under_review","warning_closed","needs_response","under_review","won","lost"],"description":"Where the dispute stands, as the processor reports it. needs_response: answer with evidence before evidence_due_at. under_review: answered, waiting on the bank. won: the money came back. lost: it did not. The warning_ states are an inquiry before any money is taken."},"evidence_due_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When evidence must reach the processor by, if the business means to fight it."},"reversal_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The reversal of the charge's payment written when the processor took the money. NULL while it has not."},"reinstatement_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The reversal of that reversal written when the processor gave the money back. NULL unless it did."},"external_id":{"type":"string","description":"The dispute's id at the processor (for Stripe, dp_...)."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","amount","reason","status","evidence_due_at","reversal_payment_id","reinstatement_payment_id","external_id","created_at"],"additionalProperties":false,"description":"A chargeback: the cardholder asked their bank for a charge's money back. When the processor takes the money, the charge's payment is reversed and the customer owes again; if the business wins, the money comes back and the reversal is undone. Evidence is answered in the processor's dashboard. Written only by the database, as the processor reports."},"CardCharge":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order the card pays toward. For a counter sale paid on the reader, NULL until the card is approved: the sale is written then, in the same transaction that records the card's payment."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically. Null for a counter sale whose card has not been approved: it has no order yet."},"counter_sale":{"type":"boolean","description":"True for a counter sale paid on the reader (recordCounterSaleOnReader): the sale is written, paid and handed over only once the card is approved; declined or called off, nothing of it is written."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order's customer, or null for an anonymous sale."},"customer_name":{"description":"The customer's name, or null for an anonymous sale.","type":["string","null"]},"channel":{"type":"string","enum":["reader","link","saved_card"],"description":"How the card is taken. reader: on a card reader. link: by a payment link the customer opens. saved_card: from a card the customer keeps on file."},"status":{"type":"string","enum":["waiting_for_card","awaiting_customer","processing","capturing","captured","declined","canceled","expired","capture_failed"],"description":"Where the charge stands. waiting_for_card: sent to the reader, waiting for the customer to present a card. awaiting_customer: a payment link sent and not yet paid. processing: a card on file being charged. capturing: the card was authorized and the payment recorded; the money is being captured. captured: done, the money is taken. declined: the card was refused. canceled: called off before any money was taken, or authorized when the order could take nothing. expired: a payment link ran out unpaid. capture_failed: the authorization lapsed before it was captured, so the payment recorded for it was reversed."},"amount_requested":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much was asked for. For a payment link left without one, NULL: what the order owes when the customer pays. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"How much was asked for. For a payment link left without one, NULL: what the order owes when the customer pays."},"amount_authorized":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the card was authorized for. NULL until it is. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the card was authorized for. NULL until it is."},"amount_captured":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What was taken from the card: what the order could take when the card was authorized, never more than was authorized. Set once captured. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What was taken from the card: what the order could take when the card was authorized, never more than was authorized. Set once captured."},"amount_taken_outside":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money the processor holds from the card for a charge that ended here without money, less what has been given back there: someone captured it in the processor's dashboard after the sale it was for could not be recorded, or after it was called off. It is not on the books and is not the business's: give it back to the card in the processor's dashboard. 0 for every other charge. Set by the system."},"amount_refundable":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What can still go back to this card: what was captured less what has been refunded (refunds that failed aside). A decimal string with up to 2 decimal places, for example \"12.50\"."},"card_reader_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a charge on a reader, the reader. NULL for every other channel."},"card_reader_name":{"description":"The reader's name, or null for a charge not on a reader.","type":["string","null"]},"reader_wait_seconds_left":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"For a charge waiting for a card at a reader: how many seconds are left before it is called off for want of one (15 minutes after it was started, by the business's clock). Null for any other charge."},"contact_point_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a payment link, the customer's email address or phone number it was sent to. NULL for every other channel."},"contact_point_value":{"description":"The email address or phone number the link went to, or null.","type":["string","null"]},"message_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a payment link, the message that carries it to the customer: queued for the sender, or held back when the customer's consent does not allow it."},"message_status":{"anyOf":[{"type":"string","enum":["queued","sending","sent","failed","suppressed"]},{"type":"null"}],"description":"Where the link's message stands: queued for the sender, sent, failed, or suppressed (held back because the customer's consent does not allow it; message_status_reason says why). Null for a charge with no message."},"message_status_reason":{"description":"Why the message failed or was suppressed.","type":["string","null"]},"link_expires_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"For a payment link, when it stops working: payment_link_valid_days (the business's policy) after it was sent."},"link_url":{"description":"The payment link itself, for the customer to open. Given only in the answer to sendPaymentLink: only its fingerprint is kept, and the message carries it.","type":["string","null"]},"card_brand":{"description":"The card's brand as the processor reports it, for example visa. NULL until a card is presented.","type":["string","null"]},"card_last4":{"description":"The last four digits of the card, which the receipt prints. Never the full number: the processor holds the card.","type":["string","null"]},"failure_reason":{"description":"Why the charge did not take money, in the processor's words or ours. NULL while it is going or once it has.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the card is taken, and where its payment is counted: the reader's store for a reader, the order's location otherwise."},"location_name":{"type":"string","description":"The store's name."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who started the charge. The payment is recorded as taken by them."},"staff_member_name":{"description":"Who started the charge, or null.","type":["string","null"]},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment recorded when the card was authorized. Set by the system."},"external_id":{"description":"The charge's id at the processor (for Stripe, the PaymentIntent, pi_...). Set once, when the processor makes it.","type":["string","null"]},"refunds":{"type":"array","items":{"$ref":"#/components/schemas/CardRefund"},"description":"Money given back to this card, oldest first."},"disputes":{"type":"array","items":{"$ref":"#/components/schemas/CardDispute"},"description":"Chargebacks on this charge, oldest first."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","sales_order_id","order_number","counter_sale","customer_id","customer_name","channel","status","amount_requested","amount_authorized","amount_captured","amount_taken_outside","amount_refundable","card_reader_id","card_reader_name","reader_wait_seconds_left","contact_point_id","contact_point_value","message_id","message_status","message_status_reason","link_expires_at","link_url","card_brand","card_last4","failure_reason","location_id","location_name","staff_member_id","staff_member_name","payment_id","external_id","refunds","disputes","created_at","updated_at","next"],"additionalProperties":false,"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted."},"CardChargePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CardCharge"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"A page of card charges."},"CardProcessorAttention":{"type":"object","properties":{"kind":{"type":"string","enum":["accounts_unset","failed_event","capture_stuck","refund_pending","dispute_open","payout_item_unmatched","order_overpaid","taken_outside"],"description":"accounts_unset: payouts cannot be recorded until the deposit account is chosen (updateCardProcessor). failed_event: news from the processor that could not be acted on (detail says why); sync again. capture_stuck: a charge recorded but not yet captured; sync again. refund_pending: a refund the processor has not confirmed. dispute_open: a chargeback to answer in the processor's dashboard. payout_item_unmatched: money a payout carried for a charge that could not be recorded, posted to other activity so the rest of the payout was; the clearing account is off by it until it is put right. order_overpaid: an order paid by card holds more than it comes to (a charge captured in the processor's dashboard is recorded whole); give the excess back to the card (refundCard). taken_outside: the processor captured money (in its dashboard) for a charge that ended here with nothing taken, such as a counter sale that could not be recorded; it is not on the books, and is given back in the processor's dashboard."},"detail":{"type":"string","description":"What it is, in words."},"card_charge_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The charge it concerns, when there is one."},"card_charge_status":{"anyOf":[{"type":"string","enum":["waiting_for_card","awaiting_customer","processing","capturing","captured","declined","canceled","expired","capture_failed"]},{"type":"null"}],"description":"That charge's status, or null when there is none."}},"required":["kind","detail","card_charge_id","card_charge_status"],"additionalProperties":false,"description":"Something about the processor that needs a person."},"CardProcessor":{"type":"object","properties":{"connected":{"type":"boolean","description":"Whether the business has a card processor connected. When false, every other field is null and cards are taken some other way."},"id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The processor account, or null when none is connected."},"name":{"description":"The processor's name as staff know it, for example Stripe. Its payment method is named after it.","type":["string","null"]},"provider":{"description":"Which processor: stripe.","type":["string","null"]},"external_account":{"description":"The account's id at the processor (for Stripe, acct_...). The processor's news for another account is refused.","type":["string","null"]},"mode":{"anyOf":[{"type":"string","enum":["test","live"]},{"type":"null"}],"description":"test for the processor's test mode, where no real card is charged; live for real money. A sandbox business is only ever in test mode."},"payment_method_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The processor's payment method, which its charges are recorded by."},"payment_method_name":{"description":"The method's name.","type":["string","null"]},"clearing_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The control account card money taken through the processor posts to, until the processor pays it out. Made by the system with the account."},"clearing_account_name":{"description":"The clearing account's name.","type":["string","null"]},"deposit_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The business's own account the processor's payouts land in, such as its bank account. NULL until it is chosen."},"deposit_account_name":{"description":"That account's name, or null.","type":["string","null"]},"fee_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The expense account the processor's fees post to when it pays out. NULL until it is chosen."},"fee_account_name":{"description":"That account's name, or null.","type":["string","null"]},"other_activity_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where payout money the processor took for anything but this system's charges posts: a web store's card sales on the same account, hardware, adjustments. NULL until it is chosen."},"other_activity_account_name":{"description":"That account's name, or null.","type":["string","null"]},"clearing_balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the clearing account holds now: card money not yet paid out. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the clearing account holds now: card money not yet paid out. Null when none is connected."},"last_event_created_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the latest piece of the processor's news read back by a sync was made, by the processor's clock: where the next sync starts."},"needs_attention":{"type":"array","items":{"$ref":"#/components/schemas/CardProcessorAttention"},"description":"What needs a person, oldest first. Empty when all is well."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["connected","id","name","provider","external_account","mode","payment_method_id","payment_method_name","clearing_account_id","clearing_account_name","deposit_account_id","deposit_account_name","fee_account_id","fee_account_name","other_activity_account_id","other_activity_account_name","clearing_balance","last_event_created_at","needs_attention","next"],"additionalProperties":false,"description":"The business's card processor: its account, mode, clearing account and what needs attention. Connected by the business's provider setup, not through the API."},"CardProcessorSync":{"type":"object","properties":{"charges_checked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many open charges and pending refunds were brought in line."},"charges_failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of those could not be, for a reason other than an outage: each was skipped so the rest were not held up."},"events_read":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many pieces of the processor's news were read back."},"events_failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of them could not be acted on: see getCardProcessor."}},"required":["charges_checked","charges_failed","events_read","events_failed"],"additionalProperties":false,"description":"What a sync with the card processor did."},"CardAgreement":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"subscription_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The subscription whose orders it lets the card be charged for."},"subscription_name":{"type":"string","description":"The subscription's name."},"amount_limit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most one cycle's order may be charged. An order that comes to more is not charged: the customer is asked to pay it another way. A decimal string with up to 2 decimal places, for example \"12.50\"."},"frequency_text":{"type":"string","description":"How often the card may be charged, in words, as the subscription ran when the customer agreed. Written by the system."},"terms_text":{"type":"string","description":"What the customer agreed to, word for word. Written by the system when the agreement is made, and never changed."},"cancel_instructions":{"type":"string","description":"How the customer stops the charges, in the business's words, for example \"Call the Boise store at 208-555-0100\"."},"agreed_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the customer agreed."},"evidence_file_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer's signature or a signed form, when one was taken (uploadFile)."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took the agreement."},"staff_member_name":{"description":"Who took the agreement, or null.","type":["string","null"]},"ended_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it stopped being in force. NULL while it is in force."},"ended_reason":{"description":"Why it ended.","type":["string","null"]},"ended_by":{"anyOf":[{"type":"string","enum":["customer","business"]},{"type":"null"}],"description":"customer: the customer cancelled it. business: the business ended it, or the card was taken off file."}},"required":["id","subscription_id","subscription_name","amount_limit","frequency_text","terms_text","cancel_instructions","agreed_at","evidence_file_id","staff_member_id","staff_member_name","ended_at","ended_reason","ended_by"],"additionalProperties":false,"description":"What a customer agreed their card on file may be charged for: each cycle of one subscription (a care plan, an auto-ship), up to a limit, until they cancel. Its terms are written by the system when it is made and never change; it can only be ended. A card on file is charged only under an agreement in force, for an order of its subscription, within its limit."},"SavedCard":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Whose card it is."},"customer_name":{"type":"string","description":"The customer's name."},"status":{"type":"string","enum":["pending","active","failed","removed"],"description":"pending: waiting for the customer to present the card at the reader. active: kept, and may be charged as its agreement allows. failed: not kept (the card was refused, or the setup was called off). removed: taken off file; it can no longer be charged."},"card_brand":{"description":"The card's brand as the processor reports it, for example visa.","type":["string","null"]},"card_last4":{"description":"The last four digits of the card, which staff and the customer know it by.","type":["string","null"]},"exp_month":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The month the card expires."},"exp_year":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The year the card expires."},"card_reader_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The reader the customer presented the card at."},"card_reader_name":{"type":"string","description":"The reader's name."},"failure_reason":{"description":"Why it was not kept, when it failed.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who set it up with the customer."},"staff_member_name":{"description":"Who set it up, or null.","type":["string","null"]},"agreements":{"type":"array","items":{"$ref":"#/components/schemas/CardAgreement"},"description":"What the customer agreed it may be charged for, oldest first."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","customer_id","customer_name","status","card_brand","card_last4","exp_month","exp_year","card_reader_id","card_reader_name","failure_reason","staff_member_id","staff_member_name","agreements","created_at","updated_at","next"],"additionalProperties":false,"description":"A customer's card kept on file at the card processor, set up on a reader in person, so a care plan or auto-ship can be charged each cycle without the customer there. It may be charged only as its card_agreement allows. The processor holds the card; nothing here holds its number. Its status moves only through the database."},"SavedCardPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SavedCard"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"A page of cards on file."},"DueChargeSkipped":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order not charged."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"why":{"type":"string","description":"Why it was not charged, and what to do instead."}},"required":["sales_order_id","order_number","why"],"additionalProperties":false,"description":"An order left uncharged, and why."},"DueCardCharges":{"type":"object","properties":{"charged":{"type":"array","items":{"$ref":"#/components/schemas/CardCharge"},"description":"The charges made, each captured or declined."},"skipped":{"type":"array","items":{"$ref":"#/components/schemas/DueChargeSkipped"},"description":"Orders owing under an agreement that were not charged, and why."}},"required":["charged","skipped"],"additionalProperties":false,"description":"What charging the cards on file for every subscription order owing did."},"CardPayoutItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"external_id":{"type":"string","description":"The movement's id at the processor (for Stripe, a balance transaction, txn_...)."},"kind":{"type":"string","enum":["charge","refund","refund_failure","dispute","dispute_reversal","other"],"description":"charge, refund, refund_failure (a refund the processor could not pay, come back), dispute (money taken for a chargeback), dispute_reversal (given back when the business won), or other (not recorded here)."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What it moved, before fees: positive into the business, negative out. For a matched item, exactly the payment it matches. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fee":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The processor's fee on it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment that recorded it here. NULL for other activity. A payment is carried by one payout at most."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"That payment's order, or null for other activity."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number, or null for other activity."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The store of that payment, where its clearing and its fee post."},"location_name":{"description":"The store's name, or null for other activity.","type":["string","null"]}},"required":["id","external_id","kind","amount","fee","payment_id","sales_order_id","order_number","location_id","location_name"],"additionalProperties":false,"description":"One movement of money a payout carried, as the processor itemizes it: a charge, a refund, a chargeback or its reversal, matched to the payment that recorded it here, or other activity. Written with its payout."},"CardPayout":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"external_id":{"type":"string","description":"The payout's id at the processor (for Stripe, po_...)."},"arrival_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the processor says the money reaches the bank."},"entry_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date it posts: its arrival_date, or the first day after the books are closed through when that is later."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What reached the bank: gross less fees plus other_activity. Negative when the processor took money from the bank. A decimal string with up to 2 decimal places, for example \"12.50\"."},"gross":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What it carried of the card money recorded here: its charges less its refunds and chargebacks, before fees. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fees":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The processor's fees on what it carried, chargeback fees among them, posted to the fee account. A decimal string with up to 2 decimal places, for example \"12.50\"."},"other_activity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What it carried that was not recorded here (another sales channel on the same account, an adjustment), posted to the other-activity account. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposit_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the money landed: the processor account's deposit account when it was paid."},"deposit_account_name":{"type":"string","description":"That account's name."},"fee_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where its fees posted: the processor account's fee account when it was paid."},"fee_account_name":{"type":"string","description":"That account's name."},"other_activity_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where its other activity posted, when it had any."},"other_activity_account_name":{"description":"That account's name, or null.","type":["string","null"]},"journal_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The journal entry it posted, or null when it moved nothing."},"items":{"type":"array","items":{"$ref":"#/components/schemas/CardPayoutItem"},"description":"What it carried, one movement at a time."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","external_id","arrival_date","entry_date","amount","gross","fees","other_activity","deposit_account_id","deposit_account_name","fee_account_id","fee_account_name","other_activity_account_id","other_activity_account_name","journal_entry_id","items","created_at"],"additionalProperties":false,"description":"Money the card processor paid out to the business's bank: what it carried of the card money recorded here, less the processor's fees, and anything else it moved on the same account. A ledger written only by the database, as the processor reports each payout paid."},"CardPayoutPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CardPayout"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"A page of the card processor's payouts."},"BooksCheck":{"type":"object","properties":{"is_consistent":{"type":"boolean","description":"True when every rule holds. False means an engine has a defect: report the rules that fail."},"checked_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the check ran, on the business's clock (UTC, ISO 8601)."},"rules":{"type":"array","items":{"type":"object","properties":{"rule":{"type":"string","description":"Stable name of the rule, for example inventory_account."},"description":{"type":"string","description":"What the rule says, in a sentence."},"expected":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The figure the rule calls for: a money total in the business's currency, or 0 for a rule that counts records that disagree. A decimal string with up to 2 decimal places, for example \"12.50\"."},"actual":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The figure found. Equal to expected when the rule holds. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_consistent":{"type":"boolean","description":"True when expected equals actual."}},"required":["rule","description","expected","actual","is_consistent"],"additionalProperties":false},"description":"Every rule, failing ones first."}},"required":["is_consistent","checked_at","rules"],"additionalProperties":false,"description":"Everything that must be true of the business's books at once: control accounts equal what they summarize, running totals equal their ledgers, the journal balances. No request can make a rule fail, so a failing rule is a defect in the system, not in the caller."},"LedgerAccount":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9.-]{0,19}$","description":"The account number or short code, unique within the tenant, for example 1200."},"name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"kind":{"type":"string","enum":["asset","liability","equity","income","cost_of_goods_sold","expense"],"description":"What the account measures. asset, cost_of_goods_sold and expense accounts grow with debits; liability, equity and income accounts grow with credits. Income less cost_of_goods_sold is gross profit. Fixed once the account exists."},"description":{"description":"What belongs in the account, for whoever (or whatever) chooses accounts for an entry.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the account is retired: it keeps its history but takes no new entries. An account with a posting_role cannot be retired."},"is_bank_account":{"type":"boolean","description":"True for an account that is a real bank account the business holds money in, as marked by whoever keeps the chart (ledger.post). A store manager brings change from it to the safe and banks takings into it with cash.manage alone; cash from any other account is a manual entry and needs ledger.post. Only an asset account the system does not post to can be one."},"writes_checks":{"type":"boolean","description":"True for the one bank account the business writes its checks on: a refund by check, account credit given back by check, and a gift card cashed out by check all come out of it. Needed only when more than one account is marked as a bank account; with one, checks are written on that one. Set by whoever keeps the chart (ledger.post). Marking one takes the mark off whichever account held it."},"posting_role":{"anyOf":[{"type":"string","enum":["inventory","opening_balance_equity","inventory_adjustment","inventory_cost_variance","accounts_receivable","customer_deposits","sales_tax_payable","sales_income","cost_of_goods_sold","accrued_purchases","inventory_in_transit","warranty_receivable","warranty_recovery","store_credit","sales_allowances","fee_income","floor_plan_interest","lender_receivable","financing_fees","lender_fees_payable","cash_drawers","cash_safe","cash_over_short","unapplied_payments","gift_card_liability","gift_card_breakage","bad_debt","trade_in_clearing","finance_charge_income"]},{"type":"null"}],"description":"Set on the one account the system posts to for a purpose: inventory (value of stock on hand), opening_balance_equity (the other side of opening balances), inventory_adjustment (count corrections, damage and loss), inventory_cost_variance (the difference when stock issued early turns out to cost more or less than assumed), accounts_receivable (what customers owe for goods already handed over), customer_deposits (what customers have paid for goods not yet handed over), sales_tax_payable (tax collected and owed to the authorities), sales_income (sales, unless the item's category names its own income account), cost_of_goods_sold (the cost of goods sold), accrued_purchases (goods received from vendors and not yet billed), inventory_in_transit (stock between two locations), warranty_receivable (what manufacturers owe for approved warranty claims), warranty_recovery (income from those claims), store_credit (what the tenant owes customers as store credit), sales_allowances (store credit given as goodwill or as a price adjustment on goods kept, which reduces sales income), fee_income (cancellation and restocking fees the business keeps), floor_plan_interest (interest paid to floor-plan lenders, added with the first lender), lender_receivable (what lenders owe for sales they financed and have not yet paid out), financing_fees (the dealer fees lenders charge for financing a sale), lender_fees_payable (dealer fees a lender bills separately and has not yet been paid), cash_drawers (cash in open register drawers), cash_safe (cash in each location's safe), cash_over_short (the difference between cash counted and what the records say should be there), unapplied_payments (money customers paid on account and not yet applied to an order), gift_card_liability (what the business owes holders of its gift cards), gift_card_breakage (gift card balances written off as never to be spent, when the business's policy allows), bad_debt (what is owed for goods handed over on a sale with no customer to collect from, once the card processor took the money back), trade_in_clearing (what customers were allowed for old units they traded in, between the unit coming into stock and the allowance paying toward their order: zero once both are done), finance_charge_income (what customers were charged for paying late, less what was waived). NULL for the tenant's own accounts, and for each floor-plan lender's own Floor Plan Payable account, which is a control account the system reaches through the lender. Set by the system only."},"is_control_account":{"type":"boolean","description":"True for an account whose balance must equal a detailed record kept elsewhere, as Inventory must equal the value of stock on hand. Only the system posts to it; manual entries are refused. Set by the system only."},"is_undeposited_funds":{"type":"boolean","description":"True for the one account the system made for cash and checks taken and not yet banked: Undeposited Funds, 1050 in a new chart. A store manager banks into it with cash.manage alone, when the accounting system records the bank deposit itself. Set by the system only, whatever the account is renamed or renumbered to."},"balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The account's balance, as a decimal string, positive on its normal side: debits less credits for asset, cost_of_goods_sold and expense accounts, credits less debits for the rest. Null without cost.view."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","code","name","kind","description","is_active","is_bank_account","writes_checks","posting_role","is_control_account","is_undeposited_funds","balance","created_at","updated_at"],"additionalProperties":false,"description":"One account in the tenant's chart of accounts. Every journal line debits or credits one. A new tenant starts with the accounts the system itself posts to and adds its own."},"LedgerAccountPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LedgerAccount"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of the chart of accounts."},"JournalEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"entry_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The accounting date the entry belongs to, in the business's own calendar. Reports by period use this, not created_at."},"memo":{"description":"What the entry is for, in words.","type":["string","null"]},"source_table":{"description":"For an entry the system posted: the table of the record that caused it, for example stock_movement. NULL for a manual entry.","type":["string","null"]},"source_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an entry the system posted: the id of the record that caused it. NULL for a manual entry."},"reverses_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The entry this one cancels, line for line. An entry can be reversed once."},"reversed_by_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The entry that cancels this one, if it has been reversed."},"is_manual":{"type":"boolean","description":"True for an entry a person posted; false for one the system posted for an event."},"lines":{"type":"array","items":{"type":"object","properties":{"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line within its entry, starting at 1."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account debited or credited."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"debit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Amount debited. Zero when the line is a credit. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Amount credited. Zero when the line is a debit. A decimal string with up to 2 decimal places, for example \"12.50\"."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location the amount belongs to, so results can be reported per location. NULL when it belongs to the business as a whole."},"location_name":{"description":"The location's name, or null when the line has none.","type":["string","null"]},"memo":{"description":"A note about this line in particular.","type":["string","null"]}},"required":["line_number","ledger_account_id","ledger_account_code","ledger_account_name","debit","credit","location_id","location_name","memo"],"additionalProperties":false},"description":"The debits and credits, in line order. They add up to the same amount."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","entry_date","memo","source_table","source_id","reverses_entry_id","reversed_by_entry_id","is_manual","lines","created_at"],"additionalProperties":false,"description":"One accounting entry: a dated set of debits and credits that add up to the same amount. Entries are only ever added. The system posts one for each event that moves money or stock value, and people post manual ones."},"JournalEntryPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/JournalEntry"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of journal entries and the cursor for the next."},"Register":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the register is at. Fixed once the register exists."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"name":{"type":"string","description":"What staff call the register, for example \"Front counter\"."},"code":{"type":"string","description":"A short code for the register, unique at its location: up to 8 capital letters and digits, for example FRONT or R2."},"is_active":{"type":"boolean","description":"False once the register is no longer used: no drawer can be opened on it. It cannot be retired with a drawer open."},"open_cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer open on the register now, or null when none is: open one with openCashDrawer."},"open_cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff quote for the drawer, unique within the business and assigned automatically. Null when no drawer is open."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","location_id","location_name","name","code","is_active","open_cash_drawer_session_id","open_cash_drawer_session_number","created_at","updated_at"],"additionalProperties":false,"description":"One till at a counter: a cash drawer that one person at a time opens, takes cash into, and closes with a count. Each drawer from opening to its count is a cash_drawer_session."},"RegisterPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Register"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of registers."},"CashPaidReason":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The reason as staff choose it, unique within the business."},"direction":{"type":"string","enum":["in","out"],"description":"out: cash leaves the drawer (a paid-out). in: cash comes into it (a paid-in). Fixed once the reason exists."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account the other side posts to: debited for a paid-out (an expense, usually), credited for a paid-in. Not a control account."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"note_required":{"type":"boolean","description":"True (the default) when each paid-in or paid-out for this reason must say what it was for, for example \"Fuel, truck 2, Chevron receipt\"."},"is_active":{"type":"boolean","description":"False once the reason is no longer used: no new cash moves for it."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","direction","ledger_account_id","ledger_account_code","ledger_account_name","note_required","is_active","created_at","updated_at"],"additionalProperties":false,"description":"Why cash goes into or out of a drawer other than by a sale, for example \"Fuel for delivery truck\" (out, to a vehicle expense account) or \"Petty cash returned\" (in). Names the account the other side posts to."},"CashPaidReasonPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CashPaidReason"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of reasons cash is paid in or out."},"CashDrawerEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sequence":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The order the drawer's entries were applied in: 1 for the first, the highest is the latest. Calculated."},"kind":{"type":"string","enum":["float","payment","receiptless_return","account_payment","gift_card","paid_in","paid_out","drop","close"],"description":"float: the cash it was opened with, from the safe. payment: cash taken on an order, or given back (a refund or a reversal, negative). account_payment: cash a customer paid on account, or given back from their account credit or for a payment on account reversed (negative). gift_card: cash taken for a gift card sold or reloaded, or given back for a sale voided or a card cashed out (negative). receiptless_return: cash given back for goods returned without a receipt. paid_in, paid_out: cash in or out for a reason other than a sale (cash_paid_reason_id). drop: cash taken out to the safe while the drawer is open. close: what it should hold, taken out as it is closed and counted into the safe."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive for cash into the drawer, negative for cash out of it. Nothing takes the drawer below zero by its records. A decimal string with up to 2 decimal places, for example \"12.50\"."},"expected_after":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the drawer should hold after this entry, in the order entries were applied (sequence). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"entered_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who put the cash in or took it out."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"note":{"description":"What it was for. Required for a paid-in or paid-out when its reason says so.","type":["string","null"]},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment, refund or reversal the cash moved for. Set for kind payment only."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order the payment was on. Null for any other kind."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number. Null for any other kind."},"receiptless_return_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The return without a receipt the cash was given back for. Set for kind receiptless_return only."},"receiptless_return_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically. Null for any other kind."},"account_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The payment on account, refund of account credit or reversal the cash moved for. Set for kind account_payment only."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer who paid on account or was given account credit back. Null for any other kind."},"customer_name":{"description":"The customer's name. Null for any other kind.","type":["string","null"]},"gift_card_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The gift card sale, reload, void or cash-out the cash moved for. Set for kind gift_card only."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number of the gift card sold, reloaded, voided or cashed out. Null for any other kind."},"cash_paid_reason_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Why the cash was paid in or out. Set for paid_in and paid_out only."},"cash_paid_reason_name":{"description":"The reason's name. Null for any other kind.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","sequence","kind","amount","expected_after","entered_on","staff_member_id","staff_member_name","note","payment_id","sales_order_id","order_number","receiptless_return_id","receiptless_return_number","account_payment_id","customer_id","customer_name","gift_card_entry_id","gift_card_number","cash_paid_reason_id","cash_paid_reason_name","created_at"],"additionalProperties":false,"description":"One amount of cash into or out of a drawer: its float, cash taken or given back on a payment or a return without a receipt, a paid-in or paid-out, a drop into the safe, and what it should hold taken out when it is closed. This is the drawer's ledger: rows are only ever added. The application writes paid-ins, paid-outs and drops; the others are written by what caused them."},"CashDrawerSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"session_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote for the drawer, unique within the business and assigned automatically."},"status":{"type":"string","enum":["open","closed","reconciled"],"description":"open: taking cash. closed: counted, and over or short by more than the business allows, waiting for a manager to reconcile it. reconciled: counted and settled, within the tolerance at once, or by a manager."},"register_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The register the drawer is in. One drawer at a time is open on a register."},"register_name":{"type":"string","description":"What staff call the register, for example \"Front counter\"."},"register_code":{"type":"string","description":"A short code for the register, unique at its location: up to 8 capital letters and digits, for example FRONT or R2."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The register's location. Cash taken there goes into a drawer open there. Calculated."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"opened_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who opened the drawer and counted the float in."},"opened_by_staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"opened_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the drawer was opened, at the location. Calculated."},"opening_float":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash put in the drawer to make change, taken out of the location's safe. 0 for a drawer opened empty. A decimal string with up to 2 decimal places, for example \"12.50\"."},"blind":{"type":"boolean","description":"True when the business counts blind (getPolicy, cash_count_blind) and this API key holds neither cash.manage nor cost.view at the drawer's location: what the drawer should hold, how far over or short it was and what moved through it are hidden (null), so the count is made without knowing the answer, and payments elsewhere do not say which drawer took them. cost.view is not blind because it reads the books, and the books show what drawers hold."},"expected_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the drawer should hold by its records: the float, plus cash taken and paid in, less cash given back, paid out and dropped. Kept as the drawer's entries are written; fixed at what it was once the drawer is closed. Hidden from whoever counts when the business counts blind. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the drawer should hold by its records: the float, plus cash taken and paid in, less cash given back, paid out and dropped. Kept as the drawer's entries are written; fixed at what it was once the drawer is closed. Hidden from whoever counts when the business counts blind. Calculated. Null while hidden (blind)."},"totals":{"anyOf":[{"type":"object","properties":{"cash_taken":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash taken in on payments, on orders and on account, and for gift cards. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cash_given_back":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash given back: refunds, reversals, account credit given back, gift card sales voided and cards cashed out, and returns without a receipt, as a positive figure. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_in":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash paid in for reasons other than a sale. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_out":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash paid out for reasons other than a sale, as a positive figure. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dropped":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash taken out to the safe while the drawer was open, as a positive figure. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["cash_taken","cash_given_back","paid_in","paid_out","dropped"],"additionalProperties":false},{"type":"null"}],"description":"What moved through the drawer, by kind. Null while the figures are hidden from the caller (blind is true)."},"closed_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who counted the drawer and closed it."},"closed_by_staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"closed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the drawer was closed, at the location. NULL while it is open. Calculated."},"counted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash counted in the drawer when it was closed, all of which goes into the safe. Never changed afterwards: a recount is recounted_cash. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cash counted in the drawer when it was closed, all of which goes into the safe. Never changed afterwards: a recount is recounted_cash."},"over_short":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"counted_cash less expected_cash: positive when the drawer held more than it should, negative when it was short. Posted to Cash Over and Short. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"counted_cash less expected_cash: positive when the drawer held more than it should, negative when it was short. Posted to Cash Over and Short. Calculated. Null while open, or while hidden (blind)."},"close_note":{"description":"Anything the person closing wants recorded, for example why it is short.","type":["string","null"]},"reconciled_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manager who reconciled a drawer closed over or short. NULL when it was reconciled at once, within the business's tolerance."},"reconciled_by_staff_member_name":{"description":"Their name, or null.","type":["string","null"]},"reconciled_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the drawer was reconciled, at the location. Calculated."},"recounted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What a manager counted when reconciling, if the cash was counted again. A figure other than counted_cash moves the difference into or out of the safe, against Cash Over and Short. NULL when it was not recounted. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What a manager counted when reconciling, if the cash was counted again. A figure other than counted_cash moves the difference into or out of the safe, against Cash Over and Short. NULL when it was not recounted."},"reconcile_note":{"description":"What the manager found: why the drawer was over or short, and what was done. Required to reconcile.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","session_number","status","register_id","register_name","register_code","location_id","location_name","opened_by_staff_member_id","opened_by_staff_member_name","opened_on","opening_float","blind","expected_cash","totals","closed_by_staff_member_id","closed_by_staff_member_name","closed_on","counted_cash","over_short","close_note","reconciled_by_staff_member_id","reconciled_by_staff_member_name","reconciled_on","recounted_cash","reconcile_note","next","created_at","updated_at"],"additionalProperties":false,"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status."},"CashDrawer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"session_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote for the drawer, unique within the business and assigned automatically."},"status":{"type":"string","enum":["open","closed","reconciled"],"description":"open: taking cash. closed: counted, and over or short by more than the business allows, waiting for a manager to reconcile it. reconciled: counted and settled, within the tolerance at once, or by a manager."},"register_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The register the drawer is in. One drawer at a time is open on a register."},"register_name":{"type":"string","description":"What staff call the register, for example \"Front counter\"."},"register_code":{"type":"string","description":"A short code for the register, unique at its location: up to 8 capital letters and digits, for example FRONT or R2."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The register's location. Cash taken there goes into a drawer open there. Calculated."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"opened_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who opened the drawer and counted the float in."},"opened_by_staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"opened_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the drawer was opened, at the location. Calculated."},"opening_float":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash put in the drawer to make change, taken out of the location's safe. 0 for a drawer opened empty. A decimal string with up to 2 decimal places, for example \"12.50\"."},"blind":{"type":"boolean","description":"True when the business counts blind (getPolicy, cash_count_blind) and this API key holds neither cash.manage nor cost.view at the drawer's location: what the drawer should hold, how far over or short it was and what moved through it are hidden (null), so the count is made without knowing the answer, and payments elsewhere do not say which drawer took them. cost.view is not blind because it reads the books, and the books show what drawers hold."},"expected_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the drawer should hold by its records: the float, plus cash taken and paid in, less cash given back, paid out and dropped. Kept as the drawer's entries are written; fixed at what it was once the drawer is closed. Hidden from whoever counts when the business counts blind. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the drawer should hold by its records: the float, plus cash taken and paid in, less cash given back, paid out and dropped. Kept as the drawer's entries are written; fixed at what it was once the drawer is closed. Hidden from whoever counts when the business counts blind. Calculated. Null while hidden (blind)."},"totals":{"anyOf":[{"type":"object","properties":{"cash_taken":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash taken in on payments, on orders and on account, and for gift cards. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cash_given_back":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash given back: refunds, reversals, account credit given back, gift card sales voided and cards cashed out, and returns without a receipt, as a positive figure. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_in":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash paid in for reasons other than a sale. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_out":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash paid out for reasons other than a sale, as a positive figure. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dropped":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash taken out to the safe while the drawer was open, as a positive figure. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["cash_taken","cash_given_back","paid_in","paid_out","dropped"],"additionalProperties":false},{"type":"null"}],"description":"What moved through the drawer, by kind. Null while the figures are hidden from the caller (blind is true)."},"closed_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who counted the drawer and closed it."},"closed_by_staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"closed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the drawer was closed, at the location. NULL while it is open. Calculated."},"counted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash counted in the drawer when it was closed, all of which goes into the safe. Never changed afterwards: a recount is recounted_cash. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cash counted in the drawer when it was closed, all of which goes into the safe. Never changed afterwards: a recount is recounted_cash."},"over_short":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"counted_cash less expected_cash: positive when the drawer held more than it should, negative when it was short. Posted to Cash Over and Short. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"counted_cash less expected_cash: positive when the drawer held more than it should, negative when it was short. Posted to Cash Over and Short. Calculated. Null while open, or while hidden (blind)."},"close_note":{"description":"Anything the person closing wants recorded, for example why it is short.","type":["string","null"]},"reconciled_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manager who reconciled a drawer closed over or short. NULL when it was reconciled at once, within the business's tolerance."},"reconciled_by_staff_member_name":{"description":"Their name, or null.","type":["string","null"]},"reconciled_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the drawer was reconciled, at the location. Calculated."},"recounted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What a manager counted when reconciling, if the cash was counted again. A figure other than counted_cash moves the difference into or out of the safe, against Cash Over and Short. NULL when it was not recounted. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What a manager counted when reconciling, if the cash was counted again. A figure other than counted_cash moves the difference into or out of the safe, against Cash Over and Short. NULL when it was not recounted."},"reconcile_note":{"description":"What the manager found: why the drawer was over or short, and what was done. Required to reconcile.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"entries":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CashDrawerEntry"}},{"type":"null"}],"description":"The drawer's ledger, in the order it was applied. Null while the figures are hidden from the caller (blind)."}},"required":["id","session_number","status","register_id","register_name","register_code","location_id","location_name","opened_by_staff_member_id","opened_by_staff_member_name","opened_on","opening_float","blind","expected_cash","totals","closed_by_staff_member_id","closed_by_staff_member_name","closed_on","counted_cash","over_short","close_note","reconciled_by_staff_member_id","reconciled_by_staff_member_name","reconciled_on","recounted_cash","reconcile_note","next","created_at","updated_at","entries"],"additionalProperties":false,"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status."},"CashDrawerPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CashDrawerSummary"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."},"totals":{"anyOf":[{"type":"object","properties":{"drawers_counted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Drawers closed and counted."},"over":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the drawers that were over held beyond what they should have, added up. A decimal string with up to 2 decimal places, for example \"12.50\"."},"short":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the drawers that were short lacked, added up, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"net_over_short":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"over less short: positive when the drawers held more than they should. A decimal string with up to 2 decimal places, for example \"12.50\"."},"covers_every_drawer":{"type":"boolean","description":"False when some drawers that match are at a location where this key does not hold cash.manage, and so are not counted here."},"by_opener":{"type":"array","items":{"type":"object","properties":{"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who opened the drawers, or null when none is recorded."},"staff_member_name":{"description":"Their name.","type":["string","null"]},"drawers_counted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Drawers closed and counted."},"over":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the drawers that were over held beyond what they should have, added up. A decimal string with up to 2 decimal places, for example \"12.50\"."},"short":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the drawers that were short lacked, added up, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"net_over_short":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"over less short: positive when the drawers held more than they should. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["staff_member_id","staff_member_name","drawers_counted","over","short","net_over_short"],"additionalProperties":false},"description":"The same, per person who opened the drawers, the most short first."}},"required":["drawers_counted","over","short","net_over_short","covers_every_drawer","by_opener"],"additionalProperties":false},{"type":"null"}],"description":"How far over or short the drawers that match were, on every page, in the end (a manager's recount where there was one), in all and per person who opened them. Counts drawers at locations where this key holds cash.manage; null when it holds it nowhere."}},"required":["data","next_cursor","totals"],"additionalProperties":false,"description":"One page of drawers, and how far over or short they all were."},"CashSafeEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sequence":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The order the safe's entries were applied in: 1 for the first, the highest is the latest. Calculated."},"kind":{"type":"string","enum":["fund","float","drop","close","recount","deposit","count","reversal"],"description":"fund: cash put in from outside the drawers (change from the bank, the first cash in the safe). float: a drawer's opening float, out. drop: cash a drawer dropped, in. close: a closed drawer's count, in. recount: the difference a manager's recount of a closed drawer found. deposit: cash taken to the bank, out. count: the difference a count of the safe found, which may be nothing. reversal: cash a payment or a payment on account took into a drawer, taken back out of the safe the drawer was closed into because it turned out not to be money (a counterfeit bill), or cash a gift card sale took, given back when the sale was voided."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive for cash into the safe, negative for cash out. Nothing takes the safe below zero by its records. For a count, the cash counted less what the safe held. Calculated for a count. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_after":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the safe held after this entry, in the order entries were applied (sequence). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"entered_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"ledger_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For fund, the account the cash came from (the bank, Opening Balance Equity for the first cash in the safe); for deposit, the account it went to (the bank, or Undeposited Funds); for reversal, the account the reversal took the money off (the cash method's). Not a control account; a deposit's is an asset. Calculated for a reversal."},"ledger_account_code":{"description":"The account's code. Null for any other kind.","type":["string","null"]},"ledger_account_name":{"description":"The account's name. Null for any other kind.","type":["string","null"]},"cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer the cash came from or went to. Set for float, drop, close, recount and reversal."},"cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The drawer's number. Null for any other kind."},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a reversal of a payment on an order, the reversal the cash was taken back out for."},"account_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a reversal of a payment on account, the reversal the cash was taken back out for."},"reference":{"description":"For a deposit, the deposit slip or bag number.","type":["string","null"]},"counted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For a count, the cash counted in the safe. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For a count, the cash counted in the safe."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who moved or counted the cash."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"note":{"description":"What it was for, or what a count found.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","sequence","kind","amount","balance_after","entered_on","ledger_account_id","ledger_account_code","ledger_account_name","cash_drawer_session_id","cash_drawer_session_number","payment_id","account_payment_id","reference","counted_cash","staff_member_id","staff_member_name","note","created_at"],"additionalProperties":false,"description":"One amount of cash into or out of a location's safe: cash put in from outside the drawers, a drawer's float out, a drop or a closed drawer's count in, a recount's difference, cash taken to the bank, the difference a count of the safe found, and cash taken back out for a payment reversed after its drawer was closed. This is the safe's ledger: rows are only ever added. The application writes funding, deposits and counts; the others are written by the drawer or the reversal that caused them."},"Check":{"type":"object","properties":{"source":{"type":"string","enum":["payment","account_payment","gift_card"],"description":"payment: on an order. account_payment: paid on account. gift_card: for a gift card sold or reloaded."},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The payment, payment on account or gift card entry the check was recorded as."},"taken_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was taken, at the location."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order it paid, for source payment. Null otherwise."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The gift card sold or reloaded, for source gift_card. Null otherwise."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who paid, when known."},"customer_name":{"description":"Their name, or null when the payer is not recorded.","type":["string","null"]},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The check's amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"The check number or whatever reference was recorded.","type":["string","null"]},"bank_deposit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The bank deposit that carried it. Null while it is not banked."},"bank_deposit_reference":{"description":"The bag or slip number of the deposit that carried it. Null while it is not banked, or when none was given.","type":["string","null"]}},"required":["source","id","taken_on","order_number","gift_card_number","customer_id","customer_name","amount","reference","bank_deposit_id","bank_deposit_reference"],"additionalProperties":false,"description":"One check taken from a customer at a location, and whether it is banked."},"CheckReversal":{"type":"object","properties":{"source":{"type":"string","enum":["payment","account_payment","gift_card"],"description":"payment: a check on an order. account_payment: a check paid on account. gift_card: a check for a gift card sold or reloaded."},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The reversal or void that took the check back."},"taken_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the check was taken: this day, or an earlier one."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order it paid, for source payment. Null otherwise."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The gift card sold or reloaded, for source gift_card. Null otherwise."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who paid, when known."},"customer_name":{"description":"Their name, or null when the payer is not recorded.","type":["string","null"]},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What of the check was taken back, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"The check number or whatever reference was recorded with the check.","type":["string","null"]}},"required":["source","id","taken_on","order_number","gift_card_number","customer_id","customer_name","amount","reference"],"additionalProperties":false,"description":"A check reversed this day: it bounced, or was handed back to the customer uncashed."},"BankDeposit":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location whose safe and checks it is."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account it went into: a bank account (is_bank_account), or the account cash and checks wait in for the bank (Undeposited Funds, 1050) when the accounting system records the bank deposit itself. Any other asset account needs ledger.post."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"cash":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash taken out of the safe for the bank. Zero for a deposit of checks only. A decimal string with up to 2 decimal places, for example \"12.50\"."},"checks_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the checks it carries add up to. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"check_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many checks it carries. Calculated."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"cash and checks_total: what the bank is to credit. A decimal string with up to 2 decimal places, for example \"12.50\"."},"safe_balance_after":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the safe held by its records once the cash was taken out: the change left for the store. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"The bag or deposit slip number people quote.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took it to the bank."},"staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"deposited_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was recorded, at the location. Calculated."},"note":{"description":"Anything worth knowing about it.","type":["string","null"]},"checks":{"type":"array","items":{"$ref":"#/components/schemas/Check"},"description":"The checks it carried, in the order they were taken."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","location_id","location_name","ledger_account_id","ledger_account_code","ledger_account_name","cash","checks_total","check_count","total","safe_balance_after","reference","staff_member_id","staff_member_name","deposited_on","note","checks","created_at"],"additionalProperties":false,"description":"One deposit taken from a location to the bank: the cash out of its safe and the checks taken there, under one bag or slip number. Rows are only ever added; the checks it carries are named when it is recorded (bank_deposit_check)."},"BankDepositPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BankDeposit"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of a location's bank deposits, newest first."},"CashSafe":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the safe is at."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"balance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash the safe holds by its records: the sum of its entries. Never negative. A decimal string with up to 2 decimal places, for example \"12.50\"."},"open_drawers":{"type":"array","items":{"type":"object","properties":{"cash_drawer_session_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"An open drawer at the location."},"cash_drawer_session_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote for the drawer, unique within the business and assigned automatically."},"register_name":{"type":"string","description":"What staff call the register, for example \"Front counter\"."},"opening_float":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The cash put in the drawer to make change, taken out of the location's safe. 0 for a drawer opened empty. A decimal string with up to 2 decimal places, for example \"12.50\"."},"expected_cash":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the drawer should hold by its records: the float, plus cash taken and paid in, less cash given back, paid out and dropped. Kept as the drawer's entries are written; fixed at what it was once the drawer is closed. Hidden from whoever counts when the business counts blind. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["cash_drawer_session_id","cash_drawer_session_number","register_name","opening_float","expected_cash"],"additionalProperties":false},"description":"The drawers open at the location now, and what each should hold: cash outside the safe."},"last_bank_deposit":{"anyOf":[{"$ref":"#/components/schemas/BankDeposit"},{"type":"null"}],"description":"The latest deposit taken from the location to the bank, or null when there has been none."},"checks_to_bank":{"type":"array","items":{"$ref":"#/components/schemas/Check"},"description":"Every check taken at the location and not yet banked, oldest first: what the next deposit carries. A check reversed or voided (it bounced, or was handed back to the customer uncashed) is left out."},"checks_to_bank_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What checks_to_bank add up to. A decimal string with up to 2 decimal places, for example \"12.50\"."},"entries":{"type":"array","items":{"$ref":"#/components/schemas/CashSafeEntry"},"description":"The safe's latest entries, up to 50, in the order they were applied: the newest is last."}},"required":["location_id","location_name","balance","open_drawers","last_bank_deposit","checks_to_bank","checks_to_bank_total","entries"],"additionalProperties":false,"description":"The safe at one location and the cash it holds. Derived: kept in step with cash_safe_entry by the database and never written directly. A location with no row has never had cash in its safe."},"Brand":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The brand name, unique within the tenant."},"is_active":{"type":"boolean","description":"False once the tenant no longer carries the brand. Its items and history stay."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","is_active","created_at","updated_at"],"additionalProperties":false,"description":"The name a product is sold under, for example Hot Spring or BioGuard. A brand is what the shopper recognizes; who the tenant buys it from is a vendor. schema.org: Brand."},"BrandPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Brand"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of brands and the cursor for the next."},"Category":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"parent_category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category this one sits under. NULL for a top-level category."},"parent_category_name":{"description":"The parent category's name, or null at the top of the tree.","type":["string","null"]},"name":{"type":"string","description":"Category name, unique among its siblings."},"path":{"type":"string","description":"The category's place in the tree, from the top: \"Water Care > Sanitizers\"."},"is_active":{"type":"boolean","description":"False once the category is retired. Items already filed under it keep it."},"income_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The income account that sales of items in this category post to. NULL uses the tenant's Sales account."},"income_account_code":{"description":"The income account's code, or null for the Sales account.","type":["string","null"]},"income_account_name":{"description":"The income account's name, or null for the Sales account.","type":["string","null"]},"cost_of_goods_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The cost of goods sold account that the cost of items in this category posts to when they are sold or returned, so margin can be read per product line. NULL uses the tenant's Cost of Goods Sold account. A change applies from then on, to returns as well: goods sold before it and brought back after come back on the new account, as their income does."},"cost_of_goods_account_code":{"description":"The cost of goods account's code, or null for the Cost of Goods Sold account.","type":["string","null"]},"cost_of_goods_account_name":{"description":"The cost of goods account's name, or null for the Cost of Goods Sold account.","type":["string","null"]},"cancellation_fee_percent":{"description":"The cancellation fee on this category's items, as a percent of what they sell for before tax, for example 25 on special-order tubs. NULL uses the business's cancellation_fee_percent. Only items filed directly under this category; a subcategory has its own. Changing it needs policy.manage.","type":["string","null"]},"restocking_fee_percent":{"description":"The restocking fee on this category's items when they come back, as a percent of what they sold for before tax. NULL uses the business's restocking_fee_percent. Only items filed directly under this category. Changing it needs policy.manage.","type":["string","null"]},"returnable_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many days after they were handed over this category's items can come back on their order, for example 30 on chemicals and 0 on custom covers. NULL (the default) takes the business's window (tenant_policy.return_days); 3650 takes them back at any time whatever the business's. A return past it needs order.cancel where the goods come back. Only items filed directly under this category. Changing it needs policy.manage."},"repurchase_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many days a customer usually goes before buying from this category again, for example 90 for filters or 30 for a sanitizer. Used to tell when a customer who has bought it only once is due to buy again. NULL (the default): no one is reported as due for it on that basis."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","parent_category_id","parent_category_name","name","path","is_active","income_account_id","income_account_code","income_account_name","cost_of_goods_account_id","cost_of_goods_account_code","cost_of_goods_account_name","cancellation_fee_percent","restocking_fee_percent","returnable_days","repurchase_days","created_at","updated_at"],"additionalProperties":false,"description":"A node in the tree items are filed under, for example Water Care, then Sanitizers. Reports group sales by it and a web store browses by it."},"CategoryPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Category"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of categories and the cursor for the next."},"VendorOffer":{"type":"object","properties":{"vendor_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The offer itself."},"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor that supplies the item."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"vendor_sku":{"description":"The vendor's own code for the item, as it must appear on an order to them.","type":["string","null"]},"cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the vendor currently charges for one unit of the item (the item's unit_text), before freight and tax. Calculated from pack_cost when the vendor quotes by the pack: pack_cost divided by pack_size, to four places. A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor currently charges for one unit of the item (the item's unit_text), before freight and tax. Calculated from pack_cost when the vendor quotes by the pack: pack_cost divided by pack_size, to four places. Null when not quoted, or when this API key lacks cost.view."},"pack_text":{"description":"What the vendor sells the item in, when it is not one unit: case, pallet, drum. Lowercase. NULL when the vendor sells single units.","type":["string","null"]},"pack_size":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units."},"pack_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size. A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size. Null also when this API key lacks cost.view."},"is_preferred":{"type":"boolean","description":"True for the vendor to order this item from by default. At most one per item."},"is_active":{"type":"boolean","description":"False once the vendor no longer supplies the item."}},"required":["vendor_item_id","vendor_id","vendor_name","vendor_sku","cost","pack_text","pack_size","pack_cost","is_preferred","is_active"],"additionalProperties":false,"description":"One vendor's offer of one item: what the vendor calls it and what it charges. An item can be bought from several vendors. This is the quoted cost used to raise a purchase order, not the cost of stock already on hand."},"ItemPack":{"type":"object","properties":{"pack_text":{"type":"string","description":"What the pack is called, as a person says it: case, bag, box. Lowercase, unique for the item. An order line names the pack by this."},"pack_size":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit (unit_text) are in one pack. A whole number for an item counted in whole units. A decimal string with up to 3 decimal places, for example \"12.500\"."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Selling price of one pack, before tax: a case is often cheaper than six bottles. NULL sells the pack at the item's price times pack_size. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Selling price of one pack, before tax: a case is often cheaper than six bottles. NULL sells the pack at the item's price times pack_size."},"effective_price":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What one pack sells for: its price, or the item's price times pack_size. Null when neither is set."},"is_active":{"type":"boolean","description":"False once the item is no longer sold in this pack. Lines already sold in it keep it."}},"required":["pack_text","pack_size","price","effective_price","is_active"],"additionalProperties":false,"description":"A pack the tenant also sells an item in, beside its own unit: a case of 6 bottles, a 25 lb bag of a chemical sold by the pound. Selling one draws pack_size of the item's own stock, so the bottle and the case are one stock. An item with no packs is sold only in its own unit. schema.org: Offer, with eligibleQuantity."},"StockAtLocation":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"Units on hand, in the item's unit. Negative when more has been issued than received and the tenant allows that. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["location_id","location_name","quantity_on_hand"],"additionalProperties":false,"description":"How much of an item is on hand at one location."},"ItemOption":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What the option is called, unique in its group whatever its case. Orders name the option by it."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where the option comes in its group, smallest first."},"price_difference":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the option adds to the model's selling price, before tax: 0 for one included in the price, negative for one that costs less. Taken onto an order line when the option is chosen, so a later change does not reprice orders already written. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost_difference":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the option adds to what the factory charges for the model, per unit: added to a purchase order line's cost when the option is ordered. Null without cost.view."},"is_active":{"type":"boolean","description":"False once the factory no longer builds it. Units already built with it keep it, and can still be sold."}},"required":["id","name","position","price_difference","cost_difference","is_active"],"additionalProperties":false,"description":"One answer to an option group, for example Sterling Marble for Shell color, with what it adds to the model's price and to what the factory charges. schema.org: PropertyValue."},"ItemOptionGroup":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What the choice is called, unique for the model whatever its case. Orders name the group by it."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where the group comes among the model's groups, smallest first."},"is_required":{"type":"boolean","description":"True when every unit is built with one of its options, so the customer must choose one (a shell color). False for an extra the customer may leave out (an audio system)."},"is_active":{"type":"boolean","description":"False once the factory no longer offers the choice. Units already built with it keep it."},"options":{"type":"array","items":{"$ref":"#/components/schemas/ItemOption"},"description":"The group's options, in position order."}},"required":["id","name","position","is_required","is_active","options"],"additionalProperties":false,"description":"One choice a serial-tracked model is built with, for example Shell color, Cabinet or Jet package. The factory builds each unit with one option from each group the customer chose from."},"ItemSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"description":{"description":"Longer description for quotes and the web store.","type":["string","null"]},"kind":{"type":"string","enum":["good","service"],"description":"good for a physical product, service for labor, fees and anything else with no physical stock. Fixed once the item exists."},"tracking":{"type":"string","enum":["none","quantity","serial"],"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},"brand_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The brand the item is sold under."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category the item is filed under."},"model":{"description":"The manufacturer's model name, for example \"Grandee\".","type":["string","null"]},"mpn":{"description":"Manufacturer part number.","type":["string","null"]},"gtin":{"description":"The barcode number printed on the product (UPC, EAN or other GTIN), digits only.","type":["string","null"]},"unit_text":{"type":"string","description":"The unit one of this item is sold in, for example each, hour or lb. Quantities and the price are per this unit."},"whole_units":{"type":"boolean","description":"True when the item is sold, bought, moved and counted only in whole units of unit_text, as a filter cartridge or a cover is: a quantity with a fraction is refused everywhere. False when part of a unit makes sense, as 2.5 lb of a chemical or 1.5 hours of labor. Always true for a serial-tracked item."},"lot_tracked":{"type":"boolean","description":"True to record the lot (batch) of everything of this item that comes in and goes out, so a recall can be traced to the customers who bought a lot and the shelves that hold it. Only for an item tracked by quantity. Turning it on for an item with stock puts that stock in its unknown lot. It can be turned off only while no lot with a code has stock."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale."},"tax_class_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"How the item is taxed. NULL uses the tenant's default class."},"is_active":{"type":"boolean","description":"False once the item is retired from sale. It stays so past sales, stock history and installed units still resolve."},"brand_name":{"description":"The brand's name, or null when the item has none.","type":["string","null"]},"tax_class_name":{"description":"The name of the tax class set on the item, or null when it uses the tenant's default.","type":["string","null"]},"category_path":{"description":"The category's place in the tree, for example \"Water Care > Sanitizers\".","type":["string","null"]},"quantity_on_hand":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"Units on hand across every location, as a decimal string. Null for an item whose stock is not counted."},"packs":{"type":"array","items":{"$ref":"#/components/schemas/ItemPack"},"description":"The packs the item is also sold in (a case of 6), by name. An order line sells one with packs and pack_text. Empty when the item is sold only in its own unit."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","sku","name","description","kind","tracking","brand_id","category_id","model","mpn","gtin","unit_text","whole_units","lot_tracked","price","tax_class_id","is_active","brand_name","tax_class_name","category_path","quantity_on_hand","packs","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer. As listed: getItem adds stock by location and who supplies it."},"Item":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"description":{"description":"Longer description for quotes and the web store.","type":["string","null"]},"kind":{"type":"string","enum":["good","service"],"description":"good for a physical product, service for labor, fees and anything else with no physical stock. Fixed once the item exists."},"tracking":{"type":"string","enum":["none","quantity","serial"],"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},"brand_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The brand the item is sold under."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category the item is filed under."},"model":{"description":"The manufacturer's model name, for example \"Grandee\".","type":["string","null"]},"mpn":{"description":"Manufacturer part number.","type":["string","null"]},"gtin":{"description":"The barcode number printed on the product (UPC, EAN or other GTIN), digits only.","type":["string","null"]},"unit_text":{"type":"string","description":"The unit one of this item is sold in, for example each, hour or lb. Quantities and the price are per this unit."},"whole_units":{"type":"boolean","description":"True when the item is sold, bought, moved and counted only in whole units of unit_text, as a filter cartridge or a cover is: a quantity with a fraction is refused everywhere. False when part of a unit makes sense, as 2.5 lb of a chemical or 1.5 hours of labor. Always true for a serial-tracked item."},"lot_tracked":{"type":"boolean","description":"True to record the lot (batch) of everything of this item that comes in and goes out, so a recall can be traced to the customers who bought a lot and the shelves that hold it. Only for an item tracked by quantity. Turning it on for an item with stock puts that stock in its unknown lot. It can be turned off only while no lot with a code has stock."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Regular selling price per unit, before tax. NULL for an item priced at the time of sale."},"tax_class_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"How the item is taxed. NULL uses the tenant's default class."},"is_active":{"type":"boolean","description":"False once the item is retired from sale. It stays so past sales, stock history and installed units still resolve."},"brand_name":{"description":"The brand's name, or null when the item has none.","type":["string","null"]},"tax_class_name":{"description":"The name of the tax class set on the item, or null when it uses the tenant's default.","type":["string","null"]},"category_path":{"description":"The category's place in the tree, for example \"Water Care > Sanitizers\".","type":["string","null"]},"quantity_on_hand":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"Units on hand across every location, as a decimal string. Null for an item whose stock is not counted."},"packs":{"type":"array","items":{"$ref":"#/components/schemas/ItemPack"},"description":"The packs the item is also sold in (a case of 6), by name. An order line sells one with packs and pack_text. Empty when the item is sold only in its own unit."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"stock":{"type":"array","items":{"$ref":"#/components/schemas/StockAtLocation"},"description":"Stock at each location the item has ever moved at. Empty for an item whose stock is not counted."},"vendors":{"type":"array","items":{"$ref":"#/components/schemas/VendorOffer"},"description":"Who supplies the item, the preferred vendor first."},"option_groups":{"type":"array","items":{"$ref":"#/components/schemas/ItemOptionGroup"},"description":"For a model built to order: the choices each unit is built with (shell color, cabinet), in position order, each with its options. An order line, a purchase order line or a unit names them in options. Empty for an item sold as it is."}},"required":["id","sku","name","description","kind","tracking","brand_id","category_id","model","mpn","gtin","unit_text","whole_units","lot_tracked","price","tax_class_id","is_active","brand_name","tax_class_name","category_path","quantity_on_hand","packs","custom_fields","created_at","updated_at","stock","vendors","option_groups"],"additionalProperties":false,"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer."},"ItemPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ItemSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of items and the cursor for the next."},"SalesChannel":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The channel's name, unique within the tenant, for example \"Web store\"."},"kind":{"type":"string","enum":["store","web","phone","field","other"],"description":"store, web, phone, field or other."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location that fulfils the channel's orders and whose stock it offers. NULL when orders are assigned a location one by one."},"is_active":{"type":"boolean","description":"False once the channel is closed."},"location_name":{"description":"The fulfilling location's name, if there is one.","type":["string","null"]},"items_listed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many items the channel offers now."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","kind","location_id","is_active","location_name","items_listed","created_at","updated_at"],"additionalProperties":false,"description":"A way orders reach the tenant: the counter, the web store, the phone, a technician in the field. Orders name their channel, so sales can be reported by it, and a web channel says which items it offers."},"SalesChannelPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SalesChannel"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of sales channels."},"ChannelListing":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"sales_channel_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The channel."},"sales_channel_name":{"type":"string","description":"The sales channel's name."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item offered."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The price on this channel, when it differs from the item's price. NULL uses the item's price."},"effective_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The price the channel shows: the listing's price, or the item's. Null when neither is set."},"is_listed":{"type":"boolean","description":"False to take the item off the channel without losing the listing."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","sales_channel_id","sales_channel_name","item_id","sku","item_name","price","effective_price","is_listed","updated_at"],"additionalProperties":false,"description":"One item offered on one sales channel, such as a product in the web store. The outside system's id for the listing is an external_link on this row."},"ChannelListingPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChannelListing"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of listings."},"CommissionPlan":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The plan's name, unique within the tenant."},"basis":{"type":"string","enum":["revenue","gross_profit"],"description":"What the rate is applied to: revenue (the selling price before tax) or gross_profit (revenue less what the goods cost)."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction: 0.05 is five percent. A category can have its own rate in commission_plan_rate. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"earned_when":{"type":"string","enum":["hand_over","paid_in_full"],"description":"When commission counts as earned: hand_over (on the day the goods are handed over) or paid_in_full (held until the order is paid for everything handed over, then earned on the day it is; held again if a payment fails, until the order is paid again). A change applies to goods handed over from then on."},"clawback_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after a hand-over a return, or a failed payment on a paid_in_full plan, still takes its earned commission back, up to 3650. NULL: always. 0: only on the day of the hand-over. Held commission on returned goods is cancelled whatever this says. A change applies to goods handed over from then on."},"floor_at_zero":{"type":"boolean","description":"True: commission on a hand-over is never below zero, so goods sold below cost on a gross_profit plan earn nothing rather than costing the salesperson. False: they earn negative commission. A change applies to goods handed over from then on."},"is_active":{"type":"boolean","description":"False once the plan is retired. A retired plan earns nothing new."},"category_rates":{"type":"array","items":{"type":"object","properties":{"item_category_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The category the rate is for."},"category_name":{"type":"string","description":"Category name, unique among its siblings."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction, applied to the plan's basis. A decimal string with up to 5 decimal places, for example \"12.50000\"."}},"required":["item_category_id","category_name","rate"],"additionalProperties":false},"description":"A plan's rate for one category of item, where it differs from the plan's own rate: hot tubs at one rate, chemicals at another. It applies to items filed directly in that category. Set the rate to 0 for a category that pays nothing."},"members":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many active staff members are paid on this plan."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","basis","rate","earned_when","clawback_days","floor_at_zero","is_active","category_rates","members","created_at","updated_at"],"additionalProperties":false,"description":"How a salesperson is paid on what they sell, for example \"Showroom sales: 20% of gross profit\". Staff are put on a plan; the plan in force when goods are handed over is the one that pays."},"CommissionPlanPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommissionPlan"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of commission plans."},"CommissionEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who earned it."},"staff_member_name":{"type":"string","description":"The staff member's name."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"sales_fulfilment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The hand-over it was earned on, or for kind return the return that took it back."},"commission_plan_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The plan the person was on at the hand-over."},"plan_name":{"type":"string","description":"The plan's name, unique within the tenant."},"kind":{"type":"string","enum":["hand_over","return","paid_in_full","payment_reversed"],"description":"What happened: hand_over (commission worked out on goods handed over), return (taken back because goods came back), paid_in_full (held commission earned because the order is now paid for everything handed over: a pair of entries, one taking it off hold and one earning it) or payment_reversed (earned commission held again because a payment failed and the order owes: a pair the other way)."},"adjusts_entry_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The hand_over entry this one changes. NULL on a hand_over entry."},"payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For payment_reversed, the reversal that failed the payment. For paid_in_full, the payment that paid the order in full, or NULL when a return left it paid for."},"share":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,4})?$","description":"The person's share of the sale at the time, from sales_order_credit. A decimal string with up to 4 decimal places, for example \"12.5000\"."},"basis":{"type":"string","enum":["revenue","gross_profit"],"description":"What the rate was applied to: revenue or gross_profit."},"basis_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The person's share of the revenue or gross profit of the hand-over, rounded so that the shares add up to the whole. Negative for a return. Shown to whoever may see the entry, cost.view or not: on a gross_profit plan the commission itself tells the line's gross profit, and so what the goods cost. A business that keeps costs from its salespeople pays them on revenue."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate applied, as a fraction. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The commission: the person's share of the hand-over's basis times rate, rounded to the cent so that the entries of one hand-over add up to its commission rounded once (a split can put the odd cent on one person). Negative for a return, or when goods sold below cost on a gross-profit plan that does not floor at zero, where it is 0 instead. A decimal string with up to 2 decimal places, for example \"12.50\"."},"earned_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the entry: when it was earned or taken back, or for a held entry when it was put on hold or taken off it. Pay for a period counts only entries that are not held."},"is_held":{"type":"boolean","description":"True for commission on hold because the plan pays only once the order is paid in full and it is not: not yet earned, and not counted in pay. A held entry is cancelled out by a later one when the order is paid (kind paid_in_full) or the goods come back."},"earned_when":{"type":"string","enum":["hand_over","paid_in_full"],"description":"The plan's earned_when at the hand-over: hand_over or paid_in_full."},"clawback_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last business date a return or a failed payment takes this commission back once earned: the hand-over's date plus the plan's clawback_days. NULL: always."},"floor_at_zero":{"type":"boolean","description":"The plan's floor_at_zero at the hand-over: true when commission on it was not allowed below zero."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","staff_member_id","staff_member_name","sales_order_id","order_number","sales_fulfilment_id","commission_plan_id","plan_name","kind","adjusts_entry_id","payment_id","share","basis","basis_amount","rate","amount","earned_on","is_held","earned_when","clawback_until","floor_at_zero","created_at"],"additionalProperties":false,"description":"Commission one salesperson earned on one hand-over of goods, or lost on a return, or held until the order is paid. A ledger written by the database: a person's commission for a period is the sum of their entries that are not held with earned_on in it. Each hand-over writes one entry per person (kind hand_over); every later change to it is another entry pointing at it."},"CommissionEntryPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommissionEntry"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of commission entries."},"CommissionTotals":{"type":"object","properties":{"earned_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First day counted."},"earned_to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day counted, inclusive."},"staff":{"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who earned it."},"name":{"type":"string","description":"The staff member's name."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Their commission for the period: earned less taken back on returns and failed payments. Held commission is not in it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"entries":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many entries make up amount."},"held":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Commission held now, whatever the period: worked out on goods handed over, under a plan that pays only once the order is paid in full, on orders that still owe. It counts in amount on the day its order is paid. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["staff_member_id","name","amount","entries","held"],"additionalProperties":false},"description":"One row per person with commission in the period or held now, by name."}},"required":["earned_from","earned_to","staff"],"additionalProperties":false,"description":"Commission earned in a period, per person: what payroll pays, and what is held."},"SalesQuota":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Whose target it is. NULL for a target that belongs to a whole location."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location the target is for. NULL for a person's target across all locations."},"period_start":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First business date of the period."},"period_end":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last business date of the period, inclusive."},"measure":{"type":"string","enum":["revenue","gross_profit","units"],"description":"What is counted: revenue (sales before tax), gross_profit (revenue less cost) or units (quantity handed over). A person's attainment counts their credit share of each sale."},"target":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The amount or number of units to reach in the period. A decimal string with up to 2 decimal places, for example \"12.50\"."},"item_category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Count only items filed directly in this category. NULL counts everything."},"staff_member_name":{"description":"The person's name, for a person's target.","type":["string","null"]},"location_name":{"description":"The location's name, for a location's target.","type":["string","null"]},"category_name":{"description":"The category's name, for a target on one category.","type":["string","null"]},"attained":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"How much of the target has been reached so far, from goods handed over in the period (a person's credit share of each sale). Null for a gross_profit target without cost.view, and for a target on sales this key may not see: shown for its own staff member's target, a location's target where it holds sales.view, and any target where it holds sales.view everywhere."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","staff_member_id","location_id","period_start","period_end","measure","target","item_category_id","staff_member_name","location_name","category_name","attained","created_at","updated_at"],"additionalProperties":false,"description":"A sales target for one person, one location, or one person at one location, over a period: \"Pat: 60,000 of revenue in October\", \"Boise: 12 hot tubs this quarter\". Attainment is not stored; it is added up from sales_fulfilment for the period."},"SalesQuotaPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SalesQuota"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of sales targets."},"Appointment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["delivery","service","consultation"],"description":"delivery: goods are brought and installed. service: a technician works on equipment. consultation: a meeting to advise or assess, with nothing delivered; one naming a sales_order_id is a site check before that order's delivery."},"status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"scheduled: planned. completed: it happened. cancelled: it will not happen; make a new appointment to try again. failed: the crew went but the visit did not do its job (a delivery refused or not possible); book again from it. Completed, cancelled and failed are final. To move a scheduled appointment, change its times."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who the visit is with."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_telephone":{"description":"The customer's phone number to call about the visit: their primary active one, in E.164 form. Null when none is on file.","type":["string","null"]},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the visit is: one of the customer's own sites. NULL when the customer comes to the location."},"site_name":{"description":"The site's name, or null when the customer comes to the location.","type":["string","null"]},"site_address":{"description":"The site's address on one line, for the crew.","type":["string","null"]},"access_notes":{"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location responsible: its crew goes, and a delivery leaves from its stock. The sale itself still belongs to the location that made it."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"work_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The work order the visit is for, when it is a service call. It must be the same customer's, and still open."},"work_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The work order's number, for a service call."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a site check: the sold order whose site a consultation goes to check before its delivery. The visit goes to that order's site, for its customer; the check itself is recorded with recordSiteCheck. Set when it is booked, and fixed. NULL for any other visit."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That order's number, for a site check."},"subscription_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a care-plan visit: the order of the plan's cycle it carries out (a sales order made by a service-visit subscription, still open when named). Completing the visit hands over that order's services still to hand over, which is when the plan's visit counts as sold; the visit's own time is covered by the plan and charges nothing, unless it is spent on a job the visit also names. Changeable while the visit is scheduled. NULL for any other visit."},"subscription_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That order's number, for a care-plan visit."},"subscription_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a care-plan visit: the care plan (subscription) whose visit it carries out."},"subscription_name":{"description":"The care plan's name, for a care-plan visit.","type":["string","null"]},"cycle_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a care-plan visit: the day the plan's visit it carries out fell due."},"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"scheduled_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"End of the window the customer was given."},"instructions":{"description":"What the crew needs to know beforehand: access, equipment to bring, what was promised.","type":["string","null"]},"outcome":{"description":"What happened, written afterwards.","type":["string","null"]},"completed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the appointment was completed. Set automatically."},"en_route_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew set off for it. Set once, while it is scheduled; the customer is told. The window cannot move after it."},"eta":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew expect to arrive, as told to the customer."},"arrived_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew arrived, by their device. Set once, while it is scheduled, and not before en_route_at."},"arrival_notes":{"description":"What the crew found on arrival: the unit's condition before unloading, the site as found.","type":["string","null"]},"site_override_reason":{"description":"Why the crew was sent to a site not shown ready, where the business requires a ready site (tenant_policy.dispatch_requires_ready_site). Given once, with delivery.override, when the crew sets off.","type":["string","null"]},"site_override_by":{"description":"Who sent the crew to a site not shown ready: the staff member whose key gave the reason, or the key's name.","type":["string","null"]},"redelivery_of_appointment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The failed delivery this one tries again. Set when it is booked, and fixed."},"redelivery_of_appointment_start":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the failed delivery this one tries again was booked for."},"readiness":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["ready","not_ready","unchecked"],"description":"ready: nothing is in the way. not_ready: something below is. unchecked: no site check yet, and nothing else in the way."},"ready_by":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the site must be shown ready by: the delivery's day less the business's site_ready_days_before."},"overdue":{"type":"boolean","description":"True when it is not ready and ready_by has come."},"site_check":{"anyOf":[{"type":"object","properties":{"checklist_response_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The site check that decides: the highest-numbered one of this site for an order on the delivery."},"check_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"outcome":{"type":"string","enum":["passed","failed"],"description":"passed or failed. Passed only when every required question is answered, every answer passes and each photo question has its photos: checked when the transaction commits."},"method":{"type":"string","enum":["on_site","remote"],"description":"on_site: someone was there. remote: judged from photos or a call with the customer."},"checked_by_name":{"description":"Who checked, or null when not recorded.","type":["string","null"]}},"required":["checklist_response_id","check_number","outcome","method","checked_by_name"],"additionalProperties":false},{"type":"null"}],"description":"The site check that decides, or null when there is none."},"blocking":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["no_lines","no_site_check","site_check_failed","party_needed","party_unconfirmed","party_time"],"description":"no_lines: nothing to hand over. no_site_check: the site has not been checked for this order. site_check_failed: the latest check failed. party_needed: the check says an outside party (a crane) is needed and none is booked. party_unconfirmed: a party is booked but not confirmed. party_time: a party is confirmed for the wrong time."},"detail":{"type":"string","description":"What is in the way, in words, with the numbers."}},"required":["code","detail"],"additionalProperties":false},"description":"What keeps the site from being ready. Empty when ready."}},"required":["status","ready_by","overdue","site_check","blocking"],"additionalProperties":false},{"type":"null"}],"description":"For a delivery to a site: whether the site is ready for it, worked out now. Null for other visits."},"parties":{"type":"array","items":{"type":"object","properties":{"appointment_party_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The booking of the party on this visit."},"role":{"type":"string","enum":["crane","electrician","contractor","inspector","other"],"description":"crane, electrician, contractor, inspector or other."},"party_name":{"type":"string","description":"Who they are, for example \"ABC Crane\" or \"Customer's electrician, Bob\". Defaults to the vendor's name."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The business's own supplier, when the business books and pays them (a crane company). NULL for someone the customer arranges."},"vendor_name":{"description":"The vendor's name, or null for a party the customer arranges.","type":["string","null"]},"arranged_by":{"type":"string","enum":["business","customer"],"description":"business: the business books them and is billed. customer: the customer arranges them, such as their own electrician."},"status":{"type":"string","enum":["needed","requested","confirmed","done","no_show","cancelled"],"description":"needed: not yet asked. requested: asked, not confirmed. confirmed: they have said yes to arrive_at. done: they did their part. no_show: they did not come. cancelled: called off. Done, no_show and cancelled are final. A visit that moves sends confirmed back to requested; a visit cancelled cancels the ones not yet confirmed; a visit completed marks the confirmed ones done."},"arrive_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When they are due: a crane about half an hour after the truck, inside the delivery window; the customer's electrician when their work will be done, before the delivery. Required to confirm."},"telephone":{"description":"How to reach them on the day, in E.164 form.","type":["string","null"]},"reference":{"description":"Their booking or job reference.","type":["string","null"]},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the business is billed for this visit, at most 100000.00, for reporting and for passing on to the customer when a failed delivery is charged for (tenant_policy.redelivery_party_item_id). Setting it needs purchase.manage. Not posted: the bill is paid in the accounting system. Only for a party the business arranges."},"notes":{"description":"Anything worth knowing, in words.","type":["string","null"]}},"required":["appointment_party_id","role","party_name","vendor_id","vendor_name","arranged_by","status","arrive_at","telephone","reference","cost_amount","notes"],"additionalProperties":false},"description":"Outside parties the visit depends on: a crane, the customer's electrician."},"attachments":{"type":"array","items":{"type":"object","properties":{"attachment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The attachment: withdraw or recaption it with updateAttachment."},"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file: read it with getFile."},"kind":{"type":"string","enum":["photo","signature","document"],"description":"photo (a JPEG, PNG or WebP image), signature (a PNG of the customer's signature, at most 256 KiB) or document (a PDF, such as an HOA approval)."},"caption":{"description":"What it shows, for example \"Pad from the street side\".","type":["string","null"]},"taken_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was taken, by the device: it may be before it was uploaded, never later than now."}},"required":["attachment_id","file_id","kind","caption","taken_at"],"additionalProperties":false},"description":"Photos, documents and the signature taken on the visit, oldest first. Withdrawn ones are left out."},"sign_off":{"anyOf":[{"type":"object","properties":{"result":{"type":"string","enum":["accepted","accepted_with_exceptions","refused","not_delivered"],"description":"accepted: delivered as ordered. accepted_with_exceptions: delivered, with damage, a shortage or a failed check noted in exceptions. refused: the customer would not take it. not_delivered: it could not be delivered."},"failure_reason":{"anyOf":[{"type":"string","enum":["site_not_ready","access_blocked","customer_absent","customer_refused","unit_damaged","wrong_unit","outside_party_no_show","weather","vehicle_or_crew","other"]},{"type":"null"}],"description":"Why a refused or not delivered delivery failed: site_not_ready, access_blocked, customer_absent, customer_refused, unit_damaged, wrong_unit, outside_party_no_show, weather, vehicle_or_crew or other. Required for those results, and only for them."},"exceptions":{"description":"What was wrong with what was accepted: damage, a shortage, a check that failed. Required for accepted_with_exceptions.","type":["string","null"]},"notes":{"description":"What happened, in words. Required when the delivery failed.","type":["string","null"]},"statement":{"type":"string","description":"The statement the customer signed under, as the business's policy worded it at the time (tenant_policy.delivery_acceptance_statement). Set by the system."},"signed_by_name":{"description":"The name of the person who signed, as they gave it. Given with the signature.","type":["string","null"]},"signed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When they signed, by the crew's device. Defaults to now when there is a signature."},"signature_file_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The signature's file: read it with getFile."},"customer_absent":{"type":"boolean","description":"True when the goods were left as agreed with nobody there to sign. Needs photos of where they were left, and delivery.override."},"fee_waived":{"type":"boolean","description":"True when the business chose not to charge the redelivery fee its policy calls for. Needs order.discount where the order was sold."},"charge_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order the redelivery fee went on, or null when nothing was charged."},"charged":{"type":"array","items":{"type":"object","properties":{"description":{"type":"string","description":"What was charged."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it came to, tax included. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["description","amount"],"additionalProperties":false},"description":"What the failed delivery was charged for, line by line. Empty when nothing was."},"charge_summary":{"description":"What the charge did to the customer's money, in words: taken from their deposit, or now owed. Null when nothing was charged.","type":["string","null"]}},"required":["result","failure_reason","exceptions","notes","statement","signed_by_name","signed_at","signature_file_id","customer_absent","fee_waived","charge_order_number","charged","charge_summary"],"additionalProperties":false},{"type":"null"}],"description":"How a delivery ended, once it has: the customer's sign-off, or why it failed. Null until then."},"crew":{"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is going."},"name":{"type":"string","description":"The staff member's name."},"is_lead":{"type":"boolean","description":"True for the person in charge of the visit: at most one. A completed delivery is recorded as handed over by the lead."},"route_position":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Where the visit comes in this person's day: 1 is their first stop. Set by putting their day in order (putRouteInOrder) or moving the visit (moveRouteStop); only the order counts, not the numbers. NULL until put in order, and again once the visit moves to another day or the person is taken off it: such a visit comes after those in order, by its window."},"also_booked":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"description":"Other scheduled appointments of theirs whose windows overlap this one. Allowed (a route has several customers in one window), but worth a look."},"time_off":{"anyOf":[{"type":"object","properties":{"time_off_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The time off: see it with getTimeOff."},"time_off_ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day out, inclusive: the same as starts_on for one day. At most a year after it."},"reason":{"description":"Why, in a few words, as the business wants it shown to whoever books: \"Sick\", \"Vacation\". Optional.","type":["string","null"]}},"required":["time_off_id","time_off_ends_on","reason"],"additionalProperties":false},{"type":"null"}],"description":"Time off they have on the visit's day, at its store: they are out. Allowed (they may be back for it), but someone else should usually go. Null when they are in."}},"required":["staff_member_id","name","is_lead","route_position","also_booked","time_off"],"additionalProperties":false},"description":"Who is going."},"lines":{"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line being delivered."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order the line is on."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are being delivered on this visit. 0 takes the line off the delivery. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_handed_over":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many the visit handed over, recorded when the delivery is completed: less than quantity when some were handed over another way after the visit was booked. NULL until then. Set by the system."},"still_to_hand_over":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many completing the delivery would hand over now: quantity, less what has been handed over on the order line another way since."},"order_status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees. Only an open order's goods can be handed over: a quote must be opened first, and a completed or cancelled order hands nothing more over."},"serial_number":{"description":"The serialized unit chosen for the line, if any.","type":["string","null"]}},"required":["sales_order_line_id","sales_order_id","order_number","sku","item_name","quantity","quantity_handed_over","still_to_hand_over","order_status","serial_number"],"additionalProperties":false},"description":"For a delivery: what is being handed over. Completing the delivery hands these over."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","kind","status","customer_id","customer_name","customer_telephone","site_id","site_name","site_address","access_notes","location_id","location_name","work_order_id","work_order_number","sales_order_id","order_number","subscription_order_id","subscription_order_number","subscription_id","subscription_name","cycle_due_on","scheduled_start","scheduled_end","instructions","outcome","completed_at","en_route_at","eta","arrived_at","arrival_notes","site_override_reason","site_override_by","redelivery_of_appointment_id","redelivery_of_appointment_start","readiness","parties","attachments","sign_off","crew","lines","next","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it."},"AppointmentPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Appointment"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of appointments."},"Checklist":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What the checklist is called, unique within the business whatever its case."},"purpose":{"type":"string","enum":["site_readiness","delivery","service"],"description":"site_readiness: answered by a site check before a delivery (in person or from the customer's photos). delivery: answered by the crew when they complete a delivery. service: answered by the technician on a job. Fixed once the checklist exists."},"description":{"description":"What the checklist is for, in words.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the business no longer uses it. Checks already answered on it keep it."},"in_use_as":{"anyOf":[{"type":"string","enum":["site_readiness_checklist","delivery_checklist"]},{"type":"null"}],"description":"Where the business's policy uses it (getPolicy), or null."},"items":{"type":"array","items":{"type":"object","properties":{"checklist_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The question: answer it by this id, or change it with updateChecklistItem."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where it comes on the checklist, smallest first."},"prompt":{"type":"string","description":"The question as the person answering reads it, for example \"Concrete pad poured and cured 7 days or more?\"."},"help_text":{"description":"How to check, for example \"Measure the narrowest point between the street and the pad, gates open.\"","type":["string","null"]},"answer_kind":{"type":"string","enum":["yes_no","number","text","photo"],"description":"yes_no, number, text, or photo (answered by attaching photos, at least min_photos of them)."},"unit_text":{"description":"For a number: its unit, for example \"in\" or \"amps\".","type":["string","null"]},"min_value":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"For a number: the smallest answer that passes, inclusive. NULL for no lower bound."},"max_value":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"For a number: the largest answer that passes, inclusive. NULL for no upper bound."},"required_answer":{"description":"For a yes/no question: the answer that passes. NULL when the answer is information only and either passes, for example \"Crane needed?\".","type":["boolean","null"]},"min_photos":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"For a photo question: how many photos it needs at least. Ignored for other kinds."},"is_required":{"type":"boolean","description":"True when it must be answered: a check that leaves it unanswered can only be recorded as failed."},"requires_party_role":{"anyOf":[{"type":"string","enum":["crane","electrician","contractor","inspector","other"]},{"type":"null"}],"description":"For a yes/no question: answered yes on a site check, the delivery needs an outside party in this role (a crane) booked and confirmed before the site counts as ready."},"is_active":{"type":"boolean","description":"False once the question is no longer asked. Answers already given keep it."},"answered":{"type":"boolean","description":"True once any check has answered it: its question and rules are then fixed."}},"required":["checklist_item_id","position","prompt","help_text","answer_kind","unit_text","min_value","max_value","required_answer","min_photos","is_required","requires_party_role","is_active","answered"],"additionalProperties":false},"description":"Its questions in order, retired ones included."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","purpose","description","is_active","in_use_as","items","created_at","updated_at"],"additionalProperties":false,"description":"A list of questions the business has someone answer on a job: whether a customer's site is ready for a delivery, or what the crew checks before leaving it."},"ChecklistPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Checklist"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of checklists."},"TaxExemptionCertificate":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer it exempts. Fixed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_is_tax_exempt":{"type":"boolean","description":"True when the customer buys without sales tax, for example a reseller, a contractor or a tax-exempt organization. Their sales are untaxed on a tax_exemption_certificate that covers the sale's day and state; when none does, tenant_policy.exemption_without_certificate says what happens. Recording a certificate sets it."},"certificate_number":{"type":"string","description":"The number on the certificate, or the buyer's permit or exemption number it cites, as an auditor would look it up. Fixed."},"reason":{"type":"string","enum":["resale","contractor","nonprofit","government","agricultural","manufacturing","other"],"description":"Why the customer buys without tax: resale (they sell it on), contractor (goods for an exempt job or that a contractor may buy exempt), nonprofit, government, agricultural, manufacturing (used up in making goods), or other (say what in notes). Fixed."},"issuing_region":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$","description":"The state it is good in, as a two-letter code (ID). A sale is covered only when it is taxed in that state: where it is delivered, or the store it is carried out of. NULL for a certificate good in any state, such as a multistate form. Fixed."},{"type":"null"}],"description":"The state it is good in, as a two-letter code (ID). A sale is covered only when it is taxed in that state: where it is delivered, or the store it is carried out of. NULL for a certificate good in any state, such as a multistate form. Fixed."},"effective_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day it covers. Fixed."},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day it covers. NULL when it does not expire (many states' resale certificates stay good until withdrawn). Fixed."},"in_force_today":{"type":"boolean","description":"True when it is active and today falls between effective_on and expires_on."},"days_left":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Days from today to expires_on: 0 on its last day, negative once it has expired. Null when it does not expire."},"renewed":{"type":"boolean","description":"True when another active certificate of the customer's, good in the same state or any, covers past this one's expiry: nothing to chase."},"file_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The scan or photo of the signed certificate (uploadFile), a PDF or an image. Can be added later."},"notes":{"description":"Anything an auditor or a manager would want: the job it is for, what other means.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who recorded it."},"staff_member_name":{"description":"Who recorded it, or null when no one is named.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once withdrawn as recorded in error. Only one no sale names can be withdrawn. An expired one stays active: it is still the proof behind the sales it covered."},"sales_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many sales name it as the certificate they were made untaxed on. listSalesOrders with tax_exemption_certificate_id lists them."},"open_orders":{"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The quote or order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."}},"required":["sales_order_id","order_number","status"],"additionalProperties":false},"description":"Quotes and open orders it covers that are still being written up, and so are untaxed on it now."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","customer_id","customer_name","customer_number","customer_is_tax_exempt","certificate_number","reason","issuing_region","effective_on","expires_on","in_force_today","days_left","renewed","file_id","notes","staff_member_id","staff_member_name","is_active","sales_count","open_orders","created_at","updated_at","next"],"additionalProperties":false,"description":"A certificate a customer gave the business to buy without sales tax: a resale certificate, a contractor's or an exempt organization's. A sale is untaxed on the one that covers its day and state, and names it. Never changed once recorded but for its scan and notes; one recorded in error is withdrawn while no sale names it. schema.org: Certification."},"TaxExemptionCertificatePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TaxExemptionCertificate"},"description":"Records on this page, newest first, or with expiring_within_days the soonest to expire first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of tax exemption certificates."},"ContactPoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer this address or number reaches."},"kind":{"type":"string","enum":["email","telephone"],"description":"email or telephone."},"value":{"type":"string","description":"The address or number itself: a lowercase email address, or a phone number in E.164 form such as +12085550100."},"name":{"description":"Whose it is or what it is for, for example \"Pat, mobile\" or \"Accounts payable\".","type":["string","null"]},"is_primary":{"type":"boolean","description":"True for the one to use first for its kind. At most one primary email and one primary telephone per customer."},"service_consent":{"type":"string","enum":["unknown","granted","denied"],"description":"Whether messages about the customer's own orders, deliveries and service may be sent here: unknown, granted or denied."},"marketing_consent":{"type":"string","enum":["unknown","granted","denied"],"description":"Whether promotional messages may be sent here: unknown, granted or denied. Send nothing promotional unless granted."},"consent_source":{"description":"How the current consent was obtained, for example \"signed delivery form 2026-10-02\" or \"replied STOP\".","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the address or number is known to be dead or no longer the customer's."},"undeliverable_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the email or text service reported that nothing can be delivered here (a hard bounce, a number that does not exist or cannot take texts). Set by the system; no message goes here while it is set. A landline that cannot take texts stays active for calls. Clear it (null) once the address is corrected or confirmed."},"undeliverable_reason":{"description":"What the service said, set with undeliverable_at.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","customer_id","kind","value","name","is_primary","service_consent","marketing_consent","consent_source","is_active","undeliverable_at","undeliverable_reason","created_at","updated_at"],"additionalProperties":false,"description":"One email address or phone number for a customer, with what the customer has agreed to receive on it. A household usually has several. Consent is recorded per address because the law attaches it to the number or mailbox, not the person; the audit trail holds when each consent changed and who changed it. schema.org: ContactPoint."},"Site":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer responsible for this site. Fixed once the site exists: orders, equipment and visits name the site together with its customer. When a property changes hands, retire the site and record it anew."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the customer calls the place, for example \"Home\" or \"McCall cabin\"."},"street_address":{"type":"string","minLength":1,"maxLength":500,"description":"Street address. A second line (unit, lot) follows a line break."},"address_locality":{"type":"string","minLength":1,"maxLength":200,"description":"City or town."},"address_region":{"type":"string","minLength":1,"maxLength":200,"description":"State or province code, for example ID."},"postal_code":{"type":"string","minLength":1,"maxLength":20,"description":"ZIP or postal code."},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"latitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Latitude in decimal degrees, for routing. Set together with longitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Latitude in decimal degrees, for routing. Set together with longitude."},"longitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":"Longitude in decimal degrees, for routing. Set together with latitude. A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Longitude in decimal degrees, for routing. Set together with latitude."},"access_notes":{"description":"What a crew needs to know to get in and work: gate code, dog, where the equipment sits, crane needed.","type":["string","null"]},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the site is in. Sales delivered here are taxed by it. NULL falls back to the selling location's area."},"is_active":{"type":"boolean","description":"False once the customer no longer has the place. Its history stays."},"tax_area_name":{"description":"The tax area's name, or null when the site has none.","type":["string","null"]},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","customer_id","customer_name","name","street_address","address_locality","address_region","postal_code","address_country","latitude","longitude","access_notes","tax_area_id","is_active","tax_area_name","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"A place where a customer takes delivery or has equipment installed and serviced: a home, a cabin, a rental property. Deliveries, installed equipment and service visits point at a site, not at the customer's billing address. schema.org: Place with a PostalAddress and GeoCoordinates."},"SitePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Site"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of sites and the cursor for the next."},"CustomerSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"kind":{"type":"string","enum":["person","organization"],"description":"person for an individual or household, organization for a business or other body."},"name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"given_name":{"description":"First name. Only for a person.","type":["string","null"]},"family_name":{"description":"Last name, used for sorting and lookup. Only for a person.","type":["string","null"]},"street_address":{"description":"Billing street address. A second line (suite, unit) follows a line break.","type":["string","null"]},"address_locality":{"description":"Billing city or town.","type":["string","null"]},"address_region":{"description":"Billing state or province code, for example ID.","type":["string","null"]},"postal_code":{"description":"Billing ZIP or postal code.","type":["string","null"]},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"Billing country as an ISO 3166-1 alpha-2 code."},"notes":{"description":"Free-form notes for staff. Not shown to the customer.","type":["string","null"]},"credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over."},"payment_terms_days":{"type":"integer","minimum":0,"maximum":365,"description":"How many days after goods are handed over payment falls due. 0 is due on hand-over; 30 is \"net 30\"."},"overdue_hold_released_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day the customer may have goods on account despite something overdue, when the tenant holds overdue accounts (tenant_policy.hold_when_overdue_days). NULL releases nothing. Setting it later needs credit.manage. The credit limit still applies."},"is_tax_exempt":{"type":"boolean","description":"True when the customer buys without sales tax, for example a reseller, a contractor or a tax-exempt organization. Their sales are untaxed on a tax_exemption_certificate that covers the sale's day and state; when none does, tenant_policy.exemption_without_certificate says what happens. Recording a certificate sets it."},"is_active":{"type":"boolean","description":"False for a customer the tenant no longer deals with. Their history stays."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"is_prospect":{"type":"boolean","description":"True for someone who has asked about buying (a lead) and not yet bought. A prospect is a customer like any other (contact points, consent, sites, the counter's lookup) until their first order is agreed, when the system clears it and tells customer.created. Set only when the customer is first recorded."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"primary_email":{"description":"The customer's primary email address, if they have one.","type":["string","null"]},"primary_telephone":{"description":"The customer's primary phone number, if they have one.","type":["string","null"]},"last_purchased_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day goods were handed over to them, or null if never."}},"required":["id","customer_number","kind","name","given_name","family_name","street_address","address_locality","address_region","postal_code","address_country","notes","credit_limit","payment_terms_days","overdue_hold_released_until","is_tax_exempt","is_active","custom_fields","is_prospect","created_at","updated_at","primary_email","primary_telephone","last_purchased_on"],"additionalProperties":false,"description":"A person, household or business that buys from the tenant and is billed. The address here is the billing address; where goods and service go is a site. schema.org: Person or Organization, by kind. As listed: without contact points, sites or balance. getCustomer has them all."},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"kind":{"type":"string","enum":["person","organization"],"description":"person for an individual or household, organization for a business or other body."},"name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"given_name":{"description":"First name. Only for a person.","type":["string","null"]},"family_name":{"description":"Last name, used for sorting and lookup. Only for a person.","type":["string","null"]},"street_address":{"description":"Billing street address. A second line (suite, unit) follows a line break.","type":["string","null"]},"address_locality":{"description":"Billing city or town.","type":["string","null"]},"address_region":{"description":"Billing state or province code, for example ID.","type":["string","null"]},"postal_code":{"description":"Billing ZIP or postal code.","type":["string","null"]},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"Billing country as an ISO 3166-1 alpha-2 code."},"notes":{"description":"Free-form notes for staff. Not shown to the customer.","type":["string","null"]},"credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over."},"payment_terms_days":{"type":"integer","minimum":0,"maximum":365,"description":"How many days after goods are handed over payment falls due. 0 is due on hand-over; 30 is \"net 30\"."},"overdue_hold_released_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day the customer may have goods on account despite something overdue, when the tenant holds overdue accounts (tenant_policy.hold_when_overdue_days). NULL releases nothing. Setting it later needs credit.manage. The credit limit still applies."},"is_tax_exempt":{"type":"boolean","description":"True when the customer buys without sales tax, for example a reseller, a contractor or a tax-exempt organization. Their sales are untaxed on a tax_exemption_certificate that covers the sale's day and state; when none does, tenant_policy.exemption_without_certificate says what happens. Recording a certificate sets it."},"is_active":{"type":"boolean","description":"False for a customer the tenant no longer deals with. Their history stays."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"is_prospect":{"type":"boolean","description":"True for someone who has asked about buying (a lead) and not yet bought. A prospect is a customer like any other (contact points, consent, sites, the counter's lookup) until their first order is agreed, when the system clears it and tells customer.created. Set only when the customer is first recorded."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"contact_points":{"type":"array","items":{"$ref":"#/components/schemas/ContactPoint"},"description":"Every email address and phone number, active first."},"sites":{"type":"array","items":{"$ref":"#/components/schemas/Site"},"description":"Every place they take delivery or have equipment, active first."},"tax_exemption_certificates":{"type":"array","items":{"$ref":"#/components/schemas/TaxExemptionCertificate"},"description":"Their active tax exemption certificates, newest first, each with whether it is in force today. recordTaxExemptionCertificate adds one."},"balance":{"type":"object","properties":{"amount_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Everything the customer owes: for goods handed over, and on account (owed_on_account). A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_overdue":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of amount_due that is past its due date. A decimal string with up to 2 decimal places, for example \"12.50\"."},"oldest_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The earliest due date among what is owed, on an order or on account. NULL when nothing is owed."},"deposits_held":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money held for the customer across their orders: paid for goods not yet handed over, or owed back for goods returned or called off and not yet refunded. It is what can be refunded. A decimal string with up to 2 decimal places, for example \"12.50\"."},"store_credit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Store credit the customer has to spend: owed to them, but paid out only by spending it on an order. Spend it with takePayment using the Store credit method; listStoreCredit shows where it came from. A decimal string with up to 2 decimal places, for example \"12.50\"."},"account_credit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Money the customer paid on account and not yet applied to an order. amount_due does not count it: apply it with applyAccountCredit, or give it back with refundAccountCredit. Never below zero. listAccountPayments shows where it came from and went. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_on_account":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of amount_due owed on account with no order behind it: a payment on account that did not arrive after its money had gone back to them as money, or a finance charge. takeAccountPayment collects it before anything else; it falls due like an order (amount_overdue, oldest_due_on), and the credit limit and overdue holds count it. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["amount_due","amount_overdue","oldest_due_on","deposits_held","store_credit","account_credit","owed_on_account"],"additionalProperties":false,"description":"One row per customer: what they owe, how much of it is late, and what the tenant holds of theirs: deposits, money owed back for goods returned, store credit and money paid on account not yet applied. Not stored: read from the orders, so it is always current. It is what a statement is drawn from. Orders with no customer are not in it: an anonymous sale is paid and handed over in one go."},"equipment":{"type":"array","items":{"type":"object","properties":{"equipment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The equipment."},"name":{"type":"string","description":"What to call it, for example \"Grandee hot tub\"."},"serial_number":{"description":"The serial number, for equipment not in the tenant's stock records.","type":["string","null"]},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is installed: one of the owner's own sites. NULL until known, for example a unit the customer carried away."},"site_name":{"description":"The site's name, or null when it is not at a site.","type":["string","null"]},"warranty_in_force":{"type":"boolean","description":"True when a warranty on it is in force today."}},"required":["equipment_id","name","serial_number","site_id","site_name","warranty_in_force"],"additionalProperties":false},"description":"What they own that the business services, active only. getEquipment has the warranties."},"purchases":{"type":"object","properties":{"first_purchased_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The first day goods were handed over to them, or null if never."},"last_purchased_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day goods were handed over to them, or null if never."},"year_to_date":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What they bought from January 1 of this year to today, before tax and net of returns (with or without a receipt), by the day goods changed hands. A decimal string with up to 2 decimal places, for example \"12.50\"."},"last_12_months":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What they bought in the 365 days to today, before tax and net of returns (with or without a receipt), by the day goods changed hands. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["first_purchased_on","last_purchased_on","year_to_date","last_12_months"],"additionalProperties":false,"description":"What they have bought. listSales with their customer_id lists every hand-over and return, with totals for any period."},"open_orders":{"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."},"order_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_due":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What is left to pay on it. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["sales_order_id","order_number","status","order_date","total","balance_due"],"additionalProperties":false},"description":"Their quotes and open orders, newest first. Completed and cancelled ones are in listSalesOrders."},"open_lead":{"anyOf":[{"type":"object","properties":{"lead_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lead."},"lead_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the business and assigned automatically."},"stage":{"type":"string","enum":["new","contacted","visited","quoted"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages."},"interest":{"type":"string","description":"What they are after, in their words: \"6-person spa for the back deck, wants lounger, asking about 0% financing\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The salesperson who owns it and owes its follow-ups. NULL while no one does, waiting for a manager to hand it out. Who it starts with is the business's choice (tenant_policy.lead_owner). Giving it to someone else needs lead.manage at its store."},"staff_member_name":{"description":"Who owns it, or null while no one does.","type":["string","null"]}},"required":["lead_id","lead_number","stage","interest","staff_member_id","staff_member_name"],"additionalProperties":false},{"type":"null"}],"description":"What they have asked about and not yet bought or walked away from (getLead), or null. A quote for it is linked to it (lead_id); a sale of anything else is not."},"trade_ins":{"type":"array","items":{"type":"object","properties":{"trade_in_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The trade-in."},"trade_in_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the tenant and assigned automatically."},"description":{"type":"string","description":"What the unit is, as the customer and the receipt see it, for example \"2009 Grandee\". Defaults to the year, brand and model, else the item's name."},"status":{"type":"string","enum":["agreed","received","given_back","cancelled"],"description":"agreed: the allowance is agreed and the unit not yet taken. received: the unit is in the business's hands, in stock, and its allowance paid toward the order. given_back: received, then handed back to the customer after the sale was called off or returned; its allowance was refunded. cancelled: the customer kept the unit. Only agreed becomes received or cancelled, and only received becomes given_back."},"allowance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer is allowed for the unit, paid toward the order when it is received, and what the unit comes into stock at. Set and changed only by someone who holds trade_in.appraise at the order's location, and only while the trade is agreed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order it was traded toward."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"received_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the unit was received, at the location it came into. Set by the system."}},"required":["trade_in_id","trade_in_number","description","status","allowance","sales_order_id","order_number","received_on"],"additionalProperties":false},"description":"The old units they have traded in toward sales, newest first, the last 10. listTradeIns with their customer_id has them all, and getTradeIn where each unit is now."}},"required":["id","customer_number","kind","name","given_name","family_name","street_address","address_locality","address_region","postal_code","address_country","notes","credit_limit","payment_terms_days","overdue_hold_released_until","is_tax_exempt","is_active","custom_fields","is_prospect","created_at","updated_at","contact_points","sites","tax_exemption_certificates","balance","equipment","purchases","open_orders","open_lead","trade_ins"],"additionalProperties":false,"description":"A person, household or business that buys from the tenant and is billed. The address here is the billing address; where goods and service go is a site. schema.org: Person or Organization, by kind."},"CustomerPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of customers and the cursor for the next."},"Identity":{"type":"object","properties":{"api_key":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"Who or what holds the key, for example \"Claude Desktop, front counter\"."},"kind":{"type":"string","enum":["session","agent","account","device"],"description":"session: issued when a person signs in with their password, or switches in with their PIN on a counter device; it acts for them and expires after the business's staff_session_hours (device_session_minutes for a switch). agent: issued for an AI agent or a program, acting for the person who issued it or for nobody with roles of its own. account: how an integration's account (a card processor, an email or text service) reports back; it changes only when the account's key_version moves, never through the API. device: a shared counter computer a manager enrolled; it acts for nobody, holds nothing, and can only list who may switch in there and switch a person in with their PIN. Fixed once the key exists."},"expires_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the key stops working, on the business's clock. NULL for a key that does not expire. It can move earlier (a rotation sets the old key's), never later."},"is_read_only":{"type":"boolean","description":"For an agent key: true when it may only look things up. Every request but a read (and signing itself out) is refused before anything else about it is looked at, whatever its roles or its person hold; its roles only decide what it may see. False for every other key. Fixed once the key exists."}},"required":["id","name","kind","expires_at","is_read_only"],"additionalProperties":false,"description":"The API key that made this request. The audit trail names it on every change it makes."},"staff_member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"given_name":{"type":"string","description":"First name."},"family_name":{"type":"string","description":"Last name."},"home_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location this person normally works from. NULL for staff who are not tied to one."},"home_location_name":{"description":"The name of that location, or null when they have no home location.","type":["string","null"]}},"required":["id","given_name","family_name","home_location_id","home_location_name"],"additionalProperties":false},{"type":"null"}],"description":"The staff member this key acts for, or null when it acts for nobody in particular. When set, use its id wherever a request asks for the staff_member_id of whoever did the work."},"device":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The device key's id."},"name":{"type":"string","description":"What staff call the counter computer."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"For a device key: the store the counter computer is at. People who hold a role there or everywhere switch in on it. NULL for every other key. Fixed once the key exists."},"location_name":{"type":"string","description":"The store's name."},"register_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a device key: the register (cash drawer) the counter computer takes cash into, at its store. NULL for none, and for every other key. Fixed once the key exists."},"register_name":{"description":"The register's name, or null for none.","type":["string","null"]},"card_reader_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a device key: the card reader the counter computer sends charges to. NULL for a separate card terminal, and for every other key. Fixed once the key exists."},"card_reader_name":{"description":"The card reader's name, or null for none.","type":["string","null"]},"expires_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the device stops working and is enrolled again."}},"required":["id","name","location_id","location_name","register_id","register_name","card_reader_id","card_reader_name","expires_at"],"additionalProperties":false},{"type":"null"}],"description":"The counter computer: this key when it is a device key, or the device a session was switched into on with a PIN. Sell at its store, take cash into its register and charges on its reader. Null on any other key."},"tenant":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"Legal or trading name shown to people."},"currency":{"type":"string","description":"ISO 4217 code of the one currency every money amount of this tenant is in. Money columns carry no currency of their own."},"is_sandbox":{"type":"boolean","description":"True for a tenant made for testing, training or simulation, whose data is not a real business's books. Only a sandbox can have its clock moved. Fixed once the tenant exists."},"now":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"The current time on this business's clock (UTC, ISO 8601). Every date the system records comes from this clock. It is real time except in a sandbox whose clock has been moved forward, so use this, not your own idea of today, when you work out dates."}},"required":["id","name","currency","is_sandbox","now"],"additionalProperties":false,"description":"The business this key belongs to. Every record the API returns is this business's."},"modules":{"type":"array","items":{"type":"string","description":"A module key, for example water_care."},"description":"The optional modules this business has. Operations under /v1/<module>/ answer not_found without it."},"permissions":{"type":"array","items":{"type":"object","properties":{"permission":{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"],"description":"An action the system can refuse. What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The one location the permission holds at, or null when it holds everywhere. A permission held at one location does not allow an action that touches the whole business."},"location_name":{"description":"The name of that location, or null when it holds everywhere.","type":["string","null"]}},"required":["permission","location_id","location_name"],"additionalProperties":false},"description":"What this key may do beyond what every key may do: its own roles and its staff member's, combined. An operation that needs one says so in its description and answers forbidden without it."},"discount_allowances":{"type":"array","items":{"type":"object","properties":{"max_discount_percent":{"type":"string","description":"The most its holders may take off a line without order.discount, as a percent of what the line comes to at its list price (what the store's and the customer's price lists give it, else the item's own price): a discount, a lower price, or both. A lower price a price list gives is not a discount. 10 lets a salesperson give up to 10% off on their own; more needs order.discount. NULL (the default) allows no discount without order.discount. A key holding several roles where the sale is made may give the largest of their allowances. Giving or changing an allowance needs order.discount for the whole business, as giving that permission does."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The one location the allowance holds at, or null when it holds everywhere."},"location_name":{"description":"The name of that location, or null when it holds everywhere.","type":["string","null"]}},"required":["max_discount_percent","location_id","location_name"],"additionalProperties":false},"description":"How much off a sale this key may give without order.discount, from its roles: the largest that holds where the sale is made. Empty when every discount needs order.discount."}},"required":["api_key","staff_member","device","tenant","modules","permissions","discount_allowances"],"additionalProperties":false,"description":"Who is calling, for which business, at what time on its clock, and what it may do."},"EnrolledDevice":{"type":"object","properties":{"api_key":{"description":"The counter's device key.","$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"The device key itself. Shown in this response only and never again. Keep it on the counter computer only, where the app stores it sealed; whoever has it can offer that counter's PIN pad."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["api_key","secret","next"],"additionalProperties":false,"description":"A counter computer just enrolled: its device key, with its secret shown this once."},"DeviceStaffMember":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"given_name":{"type":"string","description":"First name."},"family_name":{"type":"string","description":"Last name."},"pin_locked":{"type":"boolean","description":"True after five wrong PINs in a row: switching in with the PIN is refused on every device until the person signs in with their password (signIn) or a manager clears it (clearPin). It never lifts by itself."},"pin_length":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many digits the person's PIN has, so a counter's PIN pad knows when it is typed in full. A PIN set before the business changed its pin_length keeps its own length. NULL when there is no PIN."}},"required":["id","given_name","family_name","pin_locked","pin_length"],"additionalProperties":false,"description":"Someone who may switch in on this counter with their PIN: active, with a PIN, holding a role at its store or everywhere. Names only."},"DeviceStaffPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceStaffMember"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of the people who may switch in on this counter, by name, and the cursor for the next."},"PinSet":{"type":"object","properties":{"pin_set_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the person last set their PIN (setPin). NULL until they have one: they cannot switch in on a counter device."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["pin_set_at","next"],"additionalProperties":false,"description":"A PIN set: the person can switch in with it on any counter device at a store they work at."},"SiteCheck":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"check_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"purpose":{"type":"string","enum":["site_readiness","delivery","service"],"description":"site_readiness: whether the customer's site is ready for an order's delivery. delivery: the crew's checks at a delivery. service: the technician's checks on a job. The checklist's purpose, when it has one."},"outcome":{"type":"string","enum":["passed","failed"],"description":"passed or failed. Passed only when every required question is answered, every answer passes and each photo question has its photos: checked when the transaction commits."},"method":{"type":"string","enum":["on_site","remote"],"description":"on_site: someone was there. remote: judged from photos or a call with the customer."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Whose site or delivery was checked."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The site checked: one of the customer's own. Required for a site check, and must be where the order goes when the order names a site."},"site_name":{"description":"What the customer calls the place, for example \"Home\" or \"McCall cabin\".","type":["string","null"]},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order whose delivery the site was checked for: the tub decides what the site needs. Required for a site check."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"appointment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The visit it was done on: the site visit, or for the crew's checks the delivery itself (required then, and still scheduled)."},"appointment_start":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When that visit was booked for."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The checklist answered. NULL for a check recorded as a verdict with notes and photos, with no checklist."},"checklist_name":{"description":"What the checklist is called, unique within the business whatever its case.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who checked."},"staff_member_name":{"description":"Who checked, by name.","type":["string","null"]},"notes":{"description":"What was found, in words. Required to record a check with no checklist as passed, unless it has a photo.","type":["string","null"]},"answers":{"type":"array","items":{"type":"object","properties":{"checklist_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The question."},"prompt":{"type":"string","description":"The question as the person answering reads it, for example \"Concrete pad poured and cured 7 days or more?\"."},"answer":{"type":"string","description":"The answer, in words: yes, no, a number with its unit, the text, or how many photos."},"value_bool":{"description":"A yes or no answer as given, or null.","type":["boolean","null"]},"value_number":{"description":"A number answer as given, or null.","type":["string","null"]},"value_text":{"description":"A text answer as given, or null.","type":["string","null"]},"passed":{"description":"Whether the answer passes the question's rule. NULL when the question is information only. Calculated.","type":["boolean","null"]}},"required":["checklist_item_id","prompt","answer","value_bool","value_number","value_text","passed"],"additionalProperties":false},"description":"Each question on the checklist, in order, with its answer. A question not answered says so."},"failing":{"type":"array","items":{"type":"string"},"description":"What keeps the check from passing, one item each: a question unanswered, an answer that does not pass, photos missing."},"parties_needed":{"type":"array","items":{"type":"string","enum":["crane","electrician","contractor","inspector","other"]},"description":"The outside parties the answers say a delivery needs, such as a crane."},"photos":{"type":"array","items":{"type":"object","properties":{"attachment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The photo as attached to the check."},"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file: read it with getFile."},"kind":{"type":"string","enum":["photo","signature","document"],"description":"photo (a JPEG, PNG or WebP image), signature (a PNG of the customer's signature, at most 256 KiB) or document (a PDF, such as an HOA approval)."},"caption":{"description":"What it shows, for example \"Pad from the street side\".","type":["string","null"]},"checklist_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The question it is for, if any."},"prompt":{"description":"That question's prompt.","type":["string","null"]}},"required":["attachment_id","file_id","kind","caption","checklist_item_id","prompt"],"additionalProperties":false},"description":"The photos and documents taken with the check."},"deliveries":{"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A scheduled delivery of the order to this site."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"readiness":{"type":"string","enum":["ready","not_ready","unchecked"],"description":"Whether its site is now ready."},"ready_by":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the site must be ready by."}},"required":["appointment_id","appointment_start","readiness","ready_by"],"additionalProperties":false},"description":"The scheduled deliveries this check decides for, with their readiness now."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","check_number","purpose","outcome","method","customer_id","customer_name","site_id","site_name","sales_order_id","order_number","appointment_id","appointment_start","checklist_id","checklist_name","staff_member_id","staff_member_name","notes","answers","failing","parties_needed","photos","deliveries","next","created_at"],"additionalProperties":false,"description":"One time someone answered a checklist: a site check of a customer's site for an order's delivery (on site, or remotely from the customer's photos), or the crew's checks at a delivery. Its answers and photos are added with it and never after."},"SiteCheckPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SiteCheck"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of checks."},"OutsideParty":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The visit that depends on them. Fixed."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"appointment_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"End of the window the customer was given."},"appointment_status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"scheduled: planned. completed: it happened. cancelled: it will not happen; make a new appointment to try again. failed: the crew went but the visit did not do its job (a delivery refused or not possible); book again from it. Completed, cancelled and failed are final. To move a scheduled appointment, change its times."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who the visit is with."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_address":{"description":"The site's address on one line.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location responsible: its crew goes, and a delivery leaves from its stock. The sale itself still belongs to the location that made it."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"role":{"type":"string","enum":["crane","electrician","contractor","inspector","other"],"description":"crane, electrician, contractor, inspector or other."},"party_name":{"type":"string","description":"Who they are, for example \"ABC Crane\" or \"Customer's electrician, Bob\". Defaults to the vendor's name."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The business's own supplier, when the business books and pays them (a crane company). NULL for someone the customer arranges."},"vendor_name":{"description":"The vendor's name, or null for a party the customer arranges.","type":["string","null"]},"arranged_by":{"type":"string","enum":["business","customer"],"description":"business: the business books them and is billed. customer: the customer arranges them, such as their own electrician."},"party_status":{"type":"string","enum":["needed","requested","confirmed","done","no_show","cancelled"],"description":"needed: not yet asked. requested: asked, not confirmed. confirmed: they have said yes to arrive_at. done: they did their part. no_show: they did not come. cancelled: called off. Done, no_show and cancelled are final. A visit that moves sends confirmed back to requested; a visit cancelled cancels the ones not yet confirmed; a visit completed marks the confirmed ones done."},"arrive_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When they are due: a crane about half an hour after the truck, inside the delivery window; the customer's electrician when their work will be done, before the delivery. Required to confirm."},"telephone":{"description":"How to reach them on the day, in E.164 form.","type":["string","null"]},"reference":{"description":"Their booking or job reference.","type":["string","null"]},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the business is billed for this visit, at most 100000.00, for reporting and for passing on to the customer when a failed delivery is charged for (tenant_policy.redelivery_party_item_id). Setting it needs purchase.manage. Not posted: the bill is paid in the accounting system. Only for a party the business arranges."},"notes":{"description":"Anything worth knowing, in words.","type":["string","null"]}},"required":["id","appointment_id","appointment_start","appointment_end","appointment_status","customer_id","customer_name","site_address","location_id","location_name","role","party_name","vendor_id","vendor_name","arranged_by","party_status","arrive_at","telephone","reference","cost_amount","notes"],"additionalProperties":false,"description":"Someone outside the business who must be at, or have finished at, a customer's site for a visit: a crane, the customer's electrician, a contractor. A delivery is not ready until each is confirmed for the right time."},"OutsidePartyPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OutsideParty"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of outside parties, by when they are due."},"DeliveryEvidence":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"order_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_address":{"description":"The customer's own address on one line, when recorded.","type":["string","null"]},"site_address":{"description":"Where the order is delivered, on one line.","type":["string","null"]},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer has paid so far, less refunds. Calculated. Paid beyond fulfilled is a deposit; fulfilled beyond paid is owed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payments":{"type":"array","items":{"type":"object","properties":{"payment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The payment."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Positive for money received, negative for a refund. An order cannot be paid beyond its total, nor refunded beyond the money it holds for the customer: paid less handed over. A decimal string with up to 2 decimal places, for example \"12.50\"."},"method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"paid_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the payment, at the location. Calculated."},"reference":{"description":"The outside reference for the payment: a check number, a card processor's transaction id, a card's last four digits, a lender's approval number. Never a full card number: one is refused.","type":["string","null"]}},"required":["payment_id","amount","method_name","paid_on","reference"],"additionalProperties":false},"description":"Every payment and refund on the order, oldest first."},"attempts":{"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The delivery."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"appointment_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"End of the window the customer was given."},"status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"scheduled: planned. completed: it happened. cancelled: it will not happen; make a new appointment to try again. failed: the crew went but the visit did not do its job (a delivery refused or not possible); book again from it. Completed, cancelled and failed are final. To move a scheduled appointment, change its times."},"crew_lead_name":{"description":"Who led the crew.","type":["string","null"]},"en_route_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew set off for it. Set once, while it is scheduled; the customer is told. The window cannot move after it."},"arrived_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew arrived, by their device. Set once, while it is scheduled, and not before en_route_at."},"arrival_notes":{"description":"What the crew found on arrival: the unit's condition before unloading, the site as found.","type":["string","null"]},"signed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When they signed, by the crew's device. Defaults to now when there is a signature."},"minutes_on_site":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"From arrival to the sign-off being recorded, in minutes."},"result":{"anyOf":[{"type":"string","enum":["accepted","accepted_with_exceptions","refused","not_delivered"]},{"type":"null"}],"description":"accepted: delivered as ordered. accepted_with_exceptions: delivered, with damage, a shortage or a failed check noted in exceptions. refused: the customer would not take it. not_delivered: it could not be delivered."},"failure_reason":{"anyOf":[{"type":"string","enum":["site_not_ready","access_blocked","customer_absent","customer_refused","unit_damaged","wrong_unit","outside_party_no_show","weather","vehicle_or_crew","other"]},{"type":"null"}],"description":"Why a refused or not delivered delivery failed: site_not_ready, access_blocked, customer_absent, customer_refused, unit_damaged, wrong_unit, outside_party_no_show, weather, vehicle_or_crew or other. Required for those results, and only for them."},"statement":{"description":"The statement the customer signed under, as the business's policy worded it at the time (tenant_policy.delivery_acceptance_statement). Set by the system.","type":["string","null"]},"signed_by_name":{"description":"The name of the person who signed, as they gave it. Given with the signature.","type":["string","null"]},"signature_file_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The signature's file: read it with getFile."},"signature_sha256":{"description":"SHA-256 of the signature's bytes, to prove a copy identical.","type":["string","null"]},"customer_absent":{"description":"True when the goods were left as agreed with nobody there to sign. Needs photos of where they were left, and delivery.override.","type":["boolean","null"]},"fee_waived":{"description":"True when the business chose not to charge the redelivery fee its policy calls for. Needs order.discount where the order was sold.","type":["boolean","null"]},"recorded_by_crew_name":{"description":"Who on the crew the sign-off names as recording it.","type":["string","null"]},"recorded_by":{"description":"Whose API key recorded the sign-off (the staff member it acts for, or the key's name): the permissions that allowed a waived fee or goods left with nobody there were theirs.","type":["string","null"]},"site_override_reason":{"description":"Why the crew was sent to a site not shown ready, where the business requires a ready site (tenant_policy.dispatch_requires_ready_site). Given once, with delivery.override, when the crew sets off.","type":["string","null"]},"site_override_by":{"description":"Who sent the crew to a site not shown ready: the staff member whose key gave the reason, or the key's name.","type":["string","null"]},"signed_latitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":" A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Where it was signed off, by the crew's device: latitude in degrees."},"signed_longitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":" A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Where it was signed off: longitude in degrees."},"signed_distance_m":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How far from the site's map pin the sign-off was recorded, in metres, when both are known."},"distance_unknown_because":{"description":"Why signed_distance_m is null: nothing signed off yet, the sign-off has no location, or the site has no map pin. Null when the distance is given.","type":["string","null"]},"exceptions":{"description":"What was wrong with what was accepted: damage, a shortage, a check that failed. Required for accepted_with_exceptions.","type":["string","null"]},"notes":{"description":"What happened, in words. Required when the delivery failed.","type":["string","null"]},"photos":{"type":"array","items":{"type":"object","properties":{"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file: read it with getFile."},"sha256":{"type":"string","description":"SHA-256 of the bytes. It identifies the file: the same bytes uploaded again are the same file, and a copy can be proved identical to what was captured. Calculated."},"kind":{"type":"string","enum":["photo","signature","document"],"description":"photo (a JPEG, PNG or WebP image), signature (a PNG of the customer's signature, at most 256 KiB) or document (a PDF, such as an HOA approval)."},"caption":{"description":"What it shows, for example \"Pad from the street side\".","type":["string","null"]},"taken_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was taken, by the device: it may be before it was uploaded, never later than now."},"uploaded_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it was attached."},"latitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":" A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Where it was taken, by the device: latitude in degrees. Sent together with longitude."},"longitude":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,6})?$","description":" A decimal string with up to 6 decimal places, for example \"12.500000\"."},{"type":"null"}],"description":"Where it was taken: longitude in degrees."}},"required":["file_id","sha256","kind","caption","taken_at","uploaded_at","latitude","longitude"],"additionalProperties":false},"description":"Photos and documents of the visit, oldest first."},"parties":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["crane","electrician","contractor","inspector","other"],"description":"crane, electrician, contractor, inspector or other."},"party_name":{"type":"string","description":"Who they are, for example \"ABC Crane\" or \"Customer's electrician, Bob\". Defaults to the vendor's name."},"arranged_by":{"type":"string","enum":["business","customer"],"description":"business: the business books them and is billed. customer: the customer arranges them, such as their own electrician."},"party_status":{"type":"string","enum":["needed","requested","confirmed","done","no_show","cancelled"],"description":"needed: not yet asked. requested: asked, not confirmed. confirmed: they have said yes to arrive_at. done: they did their part. no_show: they did not come. cancelled: called off. Done, no_show and cancelled are final. A visit that moves sends confirmed back to requested; a visit cancelled cancels the ones not yet confirmed; a visit completed marks the confirmed ones done."},"arrive_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When they are due: a crane about half an hour after the truck, inside the delivery window; the customer's electrician when their work will be done, before the delivery. Required to confirm."},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the business is billed for this visit, at most 100000.00, for reporting and for passing on to the customer when a failed delivery is charged for (tenant_policy.redelivery_party_item_id). Setting it needs purchase.manage. Not posted: the bill is paid in the accounting system. Only for a party the business arranges."}},"required":["role","party_name","arranged_by","party_status","arrive_at","cost_amount"],"additionalProperties":false},"description":"Outside parties booked for the visit, such as the crane, and how each stood."},"crew_checks":{"type":"array","items":{"type":"object","properties":{"checklist_response_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The crew's check."},"check_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"checklist_name":{"description":"What the checklist is called, unique within the business whatever its case.","type":["string","null"]},"outcome":{"type":"string","enum":["passed","failed"],"description":"passed or failed. Passed only when every required question is answered, every answer passes and each photo question has its photos: checked when the transaction commits."},"answers":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","description":"The question as the person answering reads it, for example \"Concrete pad poured and cured 7 days or more?\"."},"answer":{"type":"string","description":"The answer, in words."},"passed":{"description":"Whether the answer passes the question's rule. NULL when the question is information only. Calculated.","type":["boolean","null"]}},"required":["prompt","answer","passed"],"additionalProperties":false},"description":"Each question with the crew's answer."}},"required":["checklist_response_id","check_number","checklist_name","outcome","answers"],"additionalProperties":false},"description":"The crew's answers to the business's delivery checklist at this visit, such as whether the customer was shown the controls."},"charged":{"type":"array","items":{"type":"object","properties":{"description":{"type":"string","description":"What was charged."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it came to, tax included. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fulfilled_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"When it was charged."}},"required":["description","amount","fulfilled_on"],"additionalProperties":false},"description":"What a failed visit charged on this order: the redelivery fee and any outside party's bill passed on."},"handed_over":{"type":"array","items":{"type":"object","properties":{"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many were handed over. A decimal string with up to 3 decimal places, for example \"12.500\"."},"serial_number":{"description":"The unit's serial number, for a serial-tracked item.","type":["string","null"]},"fulfilled_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it happened, at the store that made the sale (the order's location), wherever the goods left from: that store's day carries it. Sales reports, the day close, the journal and quota periods use this date. Calculated."}},"required":["sku","item_name","quantity","serial_number","fulfilled_on"],"additionalProperties":false},"description":"The goods the visit handed over of this order. Empty for a visit that failed."}},"required":["appointment_id","appointment_start","appointment_end","status","crew_lead_name","en_route_at","arrived_at","arrival_notes","signed_at","minutes_on_site","result","failure_reason","statement","signed_by_name","signature_file_id","signature_sha256","customer_absent","fee_waived","recorded_by_crew_name","recorded_by","site_override_reason","site_override_by","signed_latitude","signed_longitude","signed_distance_m","distance_unknown_because","exceptions","notes","photos","parties","crew_checks","charged","handed_over"],"additionalProperties":false},"description":"Every delivery of the order's goods, oldest first, with what each recorded."},"site_checks":{"type":"array","items":{"type":"object","properties":{"checklist_response_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The check."},"check_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"outcome":{"type":"string","enum":["passed","failed"],"description":"passed or failed. Passed only when every required question is answered, every answer passes and each photo question has its photos: checked when the transaction commits."},"method":{"type":"string","enum":["on_site","remote"],"description":"on_site: someone was there. remote: judged from photos or a call with the customer."},"checked_by_name":{"description":"Who checked.","type":["string","null"]},"checked_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it was recorded."},"failing":{"description":"What kept it from passing, in words, or null.","type":["string","null"]}},"required":["checklist_response_id","check_number","outcome","method","checked_by_name","checked_at","failing"],"additionalProperties":false},"description":"Every site check for the order, oldest first."},"messages":{"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The delivery the message was about."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"channel":{"type":"string","enum":["email","sms"],"description":"email or sms."},"message_status":{"type":"string","enum":["queued","sending","sent","failed","suppressed"],"description":"queued: waiting for send_after. sending: handed to the email or text service, its answer not yet recorded; set and cleared only by the system. sent: accepted by the provider. failed: the provider refused it, or never confirmed it; status_reason says why. suppressed: not sent, because consent does not allow it, the address is dead, or it was withdrawn (held back by hand, or its visit moved or was called off while it waited); set by the system when the message is queued or taken to send, and by an outside sender if marking it sent is refused because consent was withdrawn meanwhile. Sent, failed and suppressed are final."},"sent_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was handed to the provider. Set automatically when it becomes sent."},"template_name":{"description":"The template it was written from.","type":["string","null"]},"event_type":{"description":"What the message answered, such as appointment.en_route.","type":["string","null"]}},"required":["appointment_id","appointment_start","channel","message_status","sent_at","template_name","event_type"],"additionalProperties":false},"description":"Messages to the customer about these deliveries, oldest first."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["sales_order_id","order_number","order_date","customer_id","customer_name","customer_address","site_address","total","paid_total","payments","attempts","site_checks","messages","next"],"additionalProperties":false,"description":"Everything recorded about getting one order to the customer, for a dispute or a chargeback: the order and its payments, each delivery attempt with its times, sign-off, signature hash, photos and what it handed over, the site checks, and what the customer was told."},"DeliveryToBook":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the order goes: the customer's site a delivery must be booked to."},"site_name":{"description":"What the customer calls the place, for example \"Home\" or \"McCall cabin\".","type":["string","null"]},"site_address":{"type":"string","description":"The site's address on one line."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store the order was sold at."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"opened_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the customer agreed: the day a quote was opened, or the order date of an order written up open. A booked sale dates from here, a delivered one from its hand-overs. NULL while it is a quote, and for a quote cancelled unopened. Set by the system."},"days_waiting":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days since the order was opened, at its store."},"money_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is left to pay in money, after the trade-ins agreed on it. Often taken at the delivery. A decimal string with up to 2 decimal places, for example \"12.50\"."},"lines":{"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line: send it in scheduleAppointment's lines."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"serial_number":{"description":"The unit chosen for the line. Null for goods not tracked by serial, or no unit chosen yet.","type":["string","null"]},"needs_unit":{"type":"boolean","description":"True for goods tracked by serial number with no unit chosen yet: choose one before it goes."},"quantity_to_book":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many still need a delivery booked: the line's quantity, less what is handed over and what scheduled deliveries carry. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_booked":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many scheduled deliveries already carry. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["sales_order_line_id","sku","item_name","serial_number","needs_unit","quantity_to_book","quantity_booked"],"additionalProperties":false},"description":"The lines with something still to book, in the order's line order."},"booked":{"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A scheduled delivery carrying part of the order."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it is booked to start."}},"required":["appointment_id","appointment_start"],"additionalProperties":false},"description":"Scheduled deliveries already carrying part of this order, earliest first."},"site_status":{"type":"string","enum":["passed","failed","unchecked"],"description":"The order's latest site check that counts (made after any delivery of it failed for the site or access): passed, failed, or unchecked when there is none. A pass does not make a delivery ready by itself: outside parties count too, worked out on the delivery once booked."},"site_check":{"anyOf":[{"type":"object","properties":{"checklist_response_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The check that decides site_status."},"check_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"method":{"type":"string","enum":["on_site","remote"],"description":"on_site: someone was there. remote: judged from photos or a call with the customer."},"checked_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it was recorded, at the order's store."},"problems":{"description":"What kept it from passing, in words, or null when it passed. getSiteCheck has each answer.","type":["string","null"]}},"required":["checklist_response_id","check_number","method","checked_on","problems"],"additionalProperties":false},{"type":"null"}],"description":"The check that decides site_status, or null when unchecked."},"site_visit":{"anyOf":[{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A scheduled site check visit for the order."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it is booked to start."}},"required":["appointment_id","appointment_start"],"additionalProperties":false},{"type":"null"}],"description":"The next scheduled visit to check the order's site (a consultation naming the order), or null. Book one with scheduleAppointment, kind consultation and sales_order_id."},"failed_delivery":{"anyOf":[{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The failed delivery: book it again with scheduleRedelivery."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it was booked for."},"failure_reason":{"anyOf":[{"type":"string","enum":["site_not_ready","access_blocked","customer_absent","customer_refused","unit_damaged","wrong_unit","outside_party_no_show","weather","vehicle_or_crew","other"]},{"type":"null"}],"description":"Why a refused or not delivered delivery failed: site_not_ready, access_blocked, customer_absent, customer_refused, unit_damaged, wrong_unit, outside_party_no_show, weather, vehicle_or_crew or other. Required for those results, and only for them."}},"required":["appointment_id","appointment_start","failure_reason"],"additionalProperties":false},{"type":"null"}],"description":"The latest delivery of this order that failed and has not been booked again, or null. It counts as booked again once a redelivery of it is booked, or once scheduled deliveries carry everything of it still to hand over."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["sales_order_id","order_number","customer_id","customer_name","site_id","site_name","site_address","location_id","location_name","opened_on","days_waiting","money_due","lines","booked","site_status","site_check","site_visit","failed_delivery","next"],"additionalProperties":false,"description":"A sold order going to a customer's site with goods still to book on a delivery."},"DeliveryToBookPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryToBook"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of orders waiting for a delivery to be booked."},"OrderAwaitingGoods":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer's open order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"description":"The customer's name, or null for an order with none.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store the order was sold at."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"opened_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the customer agreed: the day a quote was opened, or the order date of an order written up open. A booked sale dates from here, a delivered one from its hand-overs. NULL while it is a quote, and for a quote cancelled unopened. Set by the system."},"is_late":{"type":"boolean","description":"True when goods a line waits on are past their vendor's date."},"not_ordered":{"type":"boolean","description":"True when a line waits on goods nothing on order covers yet."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When the last of the goods the order waits on is due, by the vendors' dates. Null when any of them has no date, or is not ordered."},"lines":{"type":"array","items":{"type":"object","properties":{"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line waiting."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity_to_fulfil":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many are still to be handed over. A decimal string with up to 3 decimal places, for example \"12.500\"."},"goods_coming":{"type":"array","items":{"$ref":"#/components/schemas/GoodsComing"},"description":"The goods on order it waits on, earliest date first."},"quantity_not_ordered":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many neither the store's stock nor anything on order covers. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["sales_order_line_id","line_number","sku","item_name","quantity_to_fulfil","goods_coming","quantity_not_ordered"],"additionalProperties":false},"description":"The order's lines waiting on goods, in line order."}},"required":["sales_order_id","order_number","customer_id","customer_name","location_id","location_name","opened_on","is_late","not_ordered","expected_on","lines"],"additionalProperties":false,"description":"A customer's open order waiting on goods from vendors, what brings them and when."},"OrderAwaitingGoodsPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrderAwaitingGoods"},"description":"Records on this page, late first, then the oldest sale."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of orders waiting on goods from vendors."},"UploadedFile":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"encoding_format":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"],"description":"The file's type (MIME): image/jpeg, image/png or image/webp for a photo, image/png for a signature, application/pdf for a document. It must match the file's first bytes."},"content_size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Size in bytes. Calculated."},"sha256":{"type":"string","description":"SHA-256 of the bytes. It identifies the file: the same bytes uploaded again are the same file, and a copy can be proved identical to what was captured. Calculated. As 64 hexadecimal digits."},"already_stored":{"type":"boolean","description":"True when the business already had these exact bytes: the same file is returned, nothing new is stored."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","encoding_format","content_size","sha256","already_stored","created_at","next"],"additionalProperties":false,"description":"A file stored, ready to attach by its id."},"File":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"encoding_format":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"],"description":"The file's type (MIME): image/jpeg, image/png or image/webp for a photo, image/png for a signature, application/pdf for a document. It must match the file's first bytes."},"content_size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Size in bytes. Calculated."},"sha256":{"type":"string","description":"SHA-256 of the bytes. It identifies the file: the same bytes uploaded again are the same file, and a copy can be proved identical to what was captured. Calculated. As 64 hexadecimal digits."},"content_base64":{"description":"The file's bytes, base64 encoded. Null when asked for without content (include_content=false).","type":["string","null"]},"attached_to":{"type":"array","items":{"type":"object","properties":{"attachment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The attachment."},"kind":{"type":"string","enum":["photo","signature","document"],"description":"photo (a JPEG, PNG or WebP image), signature (a PNG of the customer's signature, at most 256 KiB) or document (a PDF, such as an HOA approval)."},"caption":{"description":"What it shows, for example \"Pad from the street side\".","type":["string","null"]},"is_active":{"type":"boolean","description":"False once withdrawn as taken in error. Only a photo or document of a visit still scheduled, or of a job still open, can be withdrawn, never a signature or a check's photo."},"appointment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The visit it was taken on or belongs to."},"appointment_start":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When that visit was booked for."},"checklist_response_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The check it is part of. Added only with the check."},"check_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."}},"required":["attachment_id","kind","caption","is_active","appointment_id","appointment_start","checklist_response_id","check_number"],"additionalProperties":false},"description":"Where the file is attached."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","encoding_format","content_size","sha256","content_base64","attached_to","created_at"],"additionalProperties":false,"description":"The bytes of one file someone uploaded: a photo, a signature, a document. Stored once per business however often it is attached, keyed by its SHA-256, and never changed. schema.org: MediaObject."},"Attachment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file. Fixed."},"kind":{"type":"string","enum":["photo","signature","document"],"description":"photo (a JPEG, PNG or WebP image), signature (a PNG of the customer's signature, at most 256 KiB) or document (a PDF, such as an HOA approval)."},"caption":{"description":"What it shows, for example \"Pad from the street side\".","type":["string","null"]},"taken_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was taken, by the device: it may be before it was uploaded, never later than now."},"is_active":{"type":"boolean","description":"False once withdrawn as taken in error. Only a photo or document of a visit still scheduled, or of a job still open, can be withdrawn, never a signature or a check's photo."},"appointment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The visit it was taken on or belongs to."},"appointment_start":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When that visit was booked for."},"checklist_response_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The check it is part of. Added only with the check."},"check_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","file_id","kind","caption","taken_at","is_active","appointment_id","appointment_start","checklist_response_id","check_number","created_at","updated_at"],"additionalProperties":false,"description":"One file attached to a record as a photo, a signature or a document: the unit as it arrived, the pad from a site check, the customer's signature at a delivery. The file's bytes are never changed. An attachment that is part of a finished record cannot be withdrawn."},"TimeOff":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is out."},"staff_member_name":{"type":"string","description":"Who is out, by name."},"home_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location this person normally works from. NULL for staff who are not tied to one."},"home_location_name":{"description":"That location's name.","type":["string","null"]},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day out, as a calendar date where they work."},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day out, inclusive: the same as starts_on for one day. At most a year after it."},"reason":{"description":"Why, in a few words, as the business wants it shown to whoever books: \"Sick\", \"Vacation\". Optional.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once withdrawn: they came in after all, or it was recorded by mistake."},"visits":{"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A scheduled visit they are on in those days."},"kind":{"type":"string","enum":["delivery","service","consultation"],"description":"delivery: goods are brought and installed. service: a technician works on equipment. consultation: a meeting to advise or assess, with nothing delivered; one naming a sales_order_id is a site check before that order's delivery."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"appointment_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"appointment_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"End of the window the customer was given."},"others_on_it":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many other people are on the visit."}},"required":["appointment_id","kind","customer_name","location_name","appointment_start","appointment_end","others_on_it"],"additionalProperties":false},"description":"Scheduled visits they are on during the days out, by the day at each visit's store, earliest first: each needs someone else, or another day. Empty once withdrawn."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","staff_member_id","staff_member_name","home_location_id","home_location_name","starts_on","ends_on","reason","is_active","visits","next","created_at","updated_at"],"additionalProperties":false,"description":"Days a staff member is out: sick, on leave, at training. Whoever books a visit is shown it, as with an overlap, but it does not refuse the booking."},"TimeOffPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TimeOff"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of time off."},"WarrantyTerms":{"type":"object","properties":{"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The manufacturer these terms are for. Fixed."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"is_set":{"type":"boolean","description":"False while the business has set no terms for this manufacturer: every field is its default."},"filing_method":{"anyOf":[{"type":"string","enum":["portal","email","phone","fax","mail","distributor"]},{"type":"null"}],"description":"How claims are filed, for staff: portal (the manufacturer's dealer website), email, phone, fax, mail, or distributor (through the distributor the dealer buys from). NULL when not recorded."},"filing_contact":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"Where to file, for staff: the portal's address, the email address, the phone number, or the distributor's name."},{"type":"null"}],"description":"Where to file, for staff: the portal's address, the email address, the phone number, or the distributor's name."},"filing_instructions":{"anyOf":[{"type":"string","minLength":1,"maxLength":5000,"description":"Anything else staff need to file a claim with this manufacturer, in words: the dealer number to quote, the claim form to use."},{"type":"null"}],"description":"Anything else staff need to file a claim with this manufacturer, in words: the dealer number to quote, the claim form to use."},"filing_deadline_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}],"description":"How many days after the repair (the day its work order was completed) the manufacturer still accepts a claim. NULL sets no deadline. Claims and warranty work not sent in time are reported, not refused: whether a late claim is paid is the manufacturer's call."},"requires_proof_of_purchase":{"type":"boolean","description":"True when a claim must carry the customer's proof of purchase. Met by a unit the dealer sold (its sale is on record) or by proof_of_purchase_on_file on the claim."},"requires_serial_number":{"type":"boolean","description":"True when a claim must carry the equipment's serial number, recorded on the equipment or its unit."},"requires_photos":{"type":"boolean","description":"True when a claim must carry photos of the failure: photos_attached on the claim."},"requires_failure_description":{"type":"boolean","description":"True when a claim must say what failed and why: failure_description on the claim."},"requires_technician_notes":{"type":"boolean","description":"True when a claim must carry the technician's notes (readings, tests, error codes): technician_notes on the claim."},"requires_part_numbers":{"type":"boolean","description":"True when a claim must list the failed parts with their part numbers, when the repair replaced parts the customer was not charged for."},"failed_parts":{"type":"string","enum":["discard","hold","return"],"description":"What happens to the parts a warranty repair takes out. discard (the default): the dealer may throw them away. hold: keep each for failed_part_hold_days after the repair, in case the manufacturer asks for it. return: ship each back to the manufacturer, whose credit may depend on it. A claim for a repair that replaced parts the customer was not charged for lists them before it is sent, unless they may be discarded."},"failed_part_hold_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}],"description":"With failed_parts hold: how many days after the repair a failed part is kept before it may be discarded. Required with hold, and only with hold."},"return_authorization_required":{"type":"boolean","description":"True when a failed part may be shipped back only with the manufacturer's return authorization number."},"parts_basis":{"anyOf":[{"type":"string","enum":["none","cost","list"]},{"type":"null"}],"description":"What the manufacturer pays for the replacement parts on a claim: none (it supplies the part itself, or does not pay for parts), cost (what the part cost the dealer, plus parts_markup_percent), or list (the part's regular price in the catalog). NULL when not recorded; no expected reimbursement is worked out until both this and labor_basis are set."},"parts_markup_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"With parts_basis cost: the handling allowance the manufacturer adds to the dealer's cost, as a percentage. 0 by default."},"labor_basis":{"anyOf":[{"type":"string","enum":["none","actual_hours","schedule_hours"]},{"type":"null"}],"description":"How the manufacturer pays for labor: none, actual_hours (labor_rate for each hour worked: the claim's labor_hours, or else the hours of labor the customer was not charged for on the job), or schedule_hours (labor_rate for the hours the manufacturer's own labor-time schedule allows for the repair, entered as the claim's labor_hours). NULL when not recorded."},"labor_rate":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer pays per hour of labor. Required unless labor_basis is none or not recorded."},"trip_charge":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer pays for a trip to the customer's site, once per claim for work done at a site. NULL or 0: it pays no trip."},"shortfall_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the dealer absorbs what this manufacturer pays short of an approval, when the claim is closed as paid: an expense account of the dealer's own, or Warranty Recovery itself. NULL posts it against Warranty Recovery, reducing what warranty work recovered."},"shortfall_account_code":{"description":"The shortfall account's code, or null for Warranty Recovery.","type":["string","null"]},"shortfall_account_name":{"description":"The shortfall account's name, or null for Warranty Recovery.","type":["string","null"]},"updated_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the terms were last changed; null while not set."}},"required":["vendor_id","vendor_name","is_set","filing_method","filing_contact","filing_instructions","filing_deadline_days","requires_proof_of_purchase","requires_serial_number","requires_photos","requires_failure_description","requires_technician_notes","requires_part_numbers","failed_parts","failed_part_hold_days","return_authorization_required","parts_basis","parts_markup_percent","labor_basis","labor_rate","trip_charge","shortfall_account_id","shortfall_account_code","shortfall_account_name","updated_at"],"additionalProperties":false,"description":"How one manufacturer takes warranty claims, as the dealer has set it up: how and by when a claim is filed, what it must carry, what happens to the failed parts, and what the manufacturer pays. At most one per vendor. A vendor with none takes claims on the defaults: nothing required, failed parts discarded, no expected reimbursement worked out."},"FailedPart":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"warranty_claim_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The claim the part was replaced under. Fixed."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number of the work order the part came out on."},"claim_number":{"description":"The manufacturer's reference for the claim, once they give one.","type":["string","null"]},"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The manufacturer the claim is made to."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The catalog item the part is, when the business carries it. Fixed."},"sku":{"description":"The catalog item's SKU, when the part is in the catalog.","type":["string","null"]},"part_number":{"description":"The manufacturer's part number. Defaults to the item's mpn.","type":["string","null"]},"description":{"type":"string","description":"What the part is, for example \"Circulation pump\". Defaults to the item's name."},"serial_number":{"description":"The failed part's own serial number, when it has one (a pump, a controller).","type":["string","null"]},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the part failed."},"disposition":{"type":"string","enum":["discard","hold","return"],"description":"What is to happen to it: discard, hold (kept for the manufacturer's hold period after the repair), or return (shipped back to the manufacturer). Starts as the manufacturer's claim terms say; changing it, when the manufacturer says otherwise for this part, needs warranty.manage, and is a request of its own, never with the status."},"hold_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"For a held part: how many days after the repair it is kept, from the manufacturer's terms when it was set to hold, so a later change to the terms does not shorten a hold already promised. Required with hold, and only with hold."},"status":{"type":"string","enum":["on_hand","shipped","discarded"],"description":"on_hand: still at the business. shipped: sent back to the manufacturer. discarded: thrown away, once the manufacturer no longer needs it. Shipped and discarded are final. A part to return can be discarded only once its disposition says so, and a part held only once its hold is over."},"hold_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a part held for the manufacturer: the last day of its hold, hold_days after the repair. Null for other parts, or before the repair is done."},"return_authorization_number":{"description":"The manufacturer's return authorization (RA or RGA) number for shipping the part back. Required to ship it when the manufacturer's terms say so.","type":["string","null"]},"shipped_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the part was shipped back. Sent with status shipped, or today; never a future date."},"carrier":{"description":"Who carried it back, for example UPS.","type":["string","null"]},"tracking_number":{"description":"The carrier's tracking number.","type":["string","null"]},"discarded_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date it was thrown away. Set by the system."},"notes":{"description":"Anything else about the part, in words.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","warranty_claim_id","work_order_number","claim_number","vendor_id","vendor_name","item_id","sku","part_number","description","serial_number","quantity","disposition","hold_days","status","hold_until","return_authorization_number","shipped_on","carrier","tracking_number","discarded_on","notes","next","created_at","updated_at"],"additionalProperties":false,"description":"One failed part a warranty repair took out, tracked until the manufacturer has it back or it may be thrown away, so no credit is lost for a part left on a shelf."},"FailedPartPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FailedPart"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of failed parts."},"WarrantyClaim":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"work_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The work the claim is for."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"equipment_warranty_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The warranty the claim is made under."},"warranty_name":{"description":"The warranty's name, when one is named.","type":["string","null"]},"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The manufacturer the claim is made to."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"claim_number":{"description":"The manufacturer's reference for the claim, once they give one.","type":["string","null"]},"status":{"type":"string","enum":["draft","submitted","approved","denied","paid","withdrawn"],"description":"draft: being prepared. submitted: sent to the manufacturer, once it carries what the manufacturer's claim terms require. approved: they agreed to pay amount_approved, which is now owed to the tenant. denied: they refused, or withdrew an approval, which takes back what the approval posted. paid: their credit or payment has arrived (amount_paid); anything short of the approval is absorbed. withdrawn: dropped before it was sent, by the business or because its work order was cancelled; nothing was posted. A claim moves forward only: draft, submitted, then approved or denied; approved to paid or denied; a draft to withdrawn."},"failure_description":{"description":"What failed and why, as the manufacturer is told: the cause, the symptom, any error code. The customer's complaint is the work order's problem, and the repair its resolution.","type":["string","null"]},"technician_notes":{"description":"The technician's notes for the manufacturer: readings taken, tests run, what was ruled out.","type":["string","null"]},"proof_of_purchase_on_file":{"type":"boolean","description":"True once a copy of the customer's proof of purchase goes with the claim. Not needed for a unit the business sold: its sale is on record."},"photos_attached":{"type":"boolean","description":"True once photos of the failure go with the claim."},"labor_hours":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The hours of labor claimed: the hours worked, or the hours the manufacturer's labor-time schedule allows for the repair, as its terms say (warranty_claim_terms.labor_basis). NULL claims the hours of labor the customer was not charged for on the job. Fixed once the claim is sent."},"missing":{"type":"array","items":{"type":"string"},"description":"For a draft: what the manufacturer's claim terms require that the claim does not carry yet. It cannot be sent until this is empty. Empty once sent."},"expected_parts":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer's terms say it pays for the covered parts, as they stand now. Null to a key without cost.view when the claim was sent on, or its terms now say, parts paid at what they cost (parts_basis cost)."},"expected_labor":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer's terms say it pays for labor, as they stand now."},"expected_trip":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer's terms say it pays for the trip, as they stand now."},"amount_expected":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer's claim terms say it should pay for this claim (parts, labor and trip), worked out by the system when the claim is sent. NULL when its terms do not say how it pays. A claim sent without amount_claimed claims this. For a draft, what the terms say now. Null to a key without cost.view when the claim was sent on, or its terms now say, parts paid at what they cost (parts_basis cost)."},"amount_claimed":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the tenant asked for. Null to a key without cost.view when the claim was sent on, or its terms now say, parts paid at what they cost (parts_basis cost)."},"amount_approved":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer agreed to pay. Null as well to a key without cost.view when the claim was sent on, or its terms now say, parts paid at what they cost (parts_basis cost)."},"amount_paid":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer actually paid or credited, recorded with status paid; it defaults to amount_approved. Less than amount_approved closes the claim short: the difference is absorbed where the manufacturer's terms say (warranty_claim_terms.shortfall_account_id). Null as well to a key without cost.view when the claim was sent on, or its terms now say, parts paid at what they cost (parts_basis cost)."},"amount_short":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For a paid claim: how much less than its approval the manufacturer paid, absorbed by the business. Null as well to a key without cost.view when the claim was sent on, or its terms now say, parts paid at what they cost (parts_basis cost)."},"parts_basis":{"anyOf":[{"type":"string","enum":["none","cost","list"]},{"type":"null"}],"description":"What the manufacturer paid for parts on, by its claim terms when the claim was sent (warranty_claim_terms.parts_basis): none, cost or list. Set by the system as the claim is sent, and fixed. NULL for a draft, or when the terms did not say. A claim at cost carries what parts cost the business, so its amounts are shown only with cost.view."},"file_by":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day the manufacturer accepts this claim: the repair date plus its filing deadline. Null when it sets none, or the repair is not done."},"past_filing_deadline":{"type":"boolean","description":"True when the claim was not sent by file_by: still a draft after it, or sent later than it."},"days_outstanding":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"For an approved claim: days since the approval, that the manufacturer has owed the money."},"submitted_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date it was sent, at the location doing the work. Set by the system when the claim is submitted."},"resolved_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date it was approved or denied, at the location doing the work, and the date the approval or its withdrawal is posted on. Set by the system."},"paid_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the claim was recorded as paid, at the location doing the work, and the date any shortfall is posted on. Set by the system."},"notes":{"description":"Correspondence and reasons, in words.","type":["string","null"]},"failed_parts":{"type":"array","items":{"$ref":"#/components/schemas/FailedPart"},"description":"The failed parts tracked on this claim."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","work_order_id","work_order_number","equipment_warranty_id","warranty_name","vendor_id","vendor_name","claim_number","status","failure_description","technician_notes","proof_of_purchase_on_file","photos_attached","labor_hours","missing","expected_parts","expected_labor","expected_trip","amount_expected","amount_claimed","amount_approved","amount_paid","amount_short","parts_basis","file_by","past_filing_deadline","days_outstanding","submitted_on","resolved_on","paid_on","notes","failed_parts","next","created_at","updated_at"],"additionalProperties":false,"description":"A claim on a manufacturer to pay for warranty work the tenant did: the parts and labor on one work order. It tracks the claim from draft to paid or denied, so warranty work the tenant is owed for is never forgotten."},"WarrantyClaimPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WarrantyClaim"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of warranty claims."},"WarrantySummary":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today's business date, which ages are counted to."},"manufacturers":{"type":"array","items":{"type":"object","properties":{"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The manufacturer."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"terms_set":{"type":"boolean","description":"Whether the business has set this manufacturer's claim terms."},"drafts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Claims being prepared, not yet sent."},"drafts_past_deadline":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Drafts whose filing deadline has passed."},"unclaimed_work_orders":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Completed warranty work on equipment under this manufacturer's warranty that has no claim (a withdrawn one aside). A job under two manufacturers' warranties counts under each until either is claimed."},"unclaimed_past_deadline":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of those, how many are past this manufacturer's filing deadline."},"submitted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Claims sent and waiting on the manufacturer's answer."},"amount_awaiting":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the submitted claims ask for. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."},"approved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Claims approved and not yet paid."},"amount_owed":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the manufacturer owes on approved claims. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."},"owed_0_30":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Owed on approvals 0 to 30 days old. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."},"owed_31_60":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Owed on approvals 31 to 60 days old. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."},"owed_61_90":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Owed on approvals 61 to 90 days old. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."},"owed_over_90":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Owed on approvals more than 90 days old. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."},"parts_to_return":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Failed parts on hand that go back to the manufacturer."},"parts_held":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Failed parts on hand held for the manufacturer."},"amount_absorbed":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What claims closed short of their approval left the business to absorb, all time. Null to a key without cost.view when any of this manufacturer's claims was sent on, or its terms now say, parts paid at what they cost: the total would carry what parts cost the business."}},"required":["vendor_id","vendor_name","terms_set","drafts","drafts_past_deadline","unclaimed_work_orders","unclaimed_past_deadline","submitted","amount_awaiting","approved","amount_owed","owed_0_30","owed_31_60","owed_61_90","owed_over_90","parts_to_return","parts_held","amount_absorbed"],"additionalProperties":false},"description":"One row per manufacturer with claims, terms or unclaimed warranty work, by name. A manufacturer with nothing open still appears if it has terms."}},"required":["as_of","manufacturers"],"additionalProperties":false,"description":"Where warranty money stands with each manufacturer: what is unsent, waiting, owed and how old."},"JobTemplate":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","description":"The short code staff use, for example WINTERIZE: 2 to 20 uppercase letters, digits or hyphens, unique within the business."},"name":{"type":"string","description":"What the job is called, for example \"Spa winterize\"."},"description":{"description":"What the job involves, in words.","type":["string","null"]},"problem_text":{"description":"What a work order opened from it says was asked for when none is given, for example \"Winterize spa for the season\".","type":["string","null"]},"labor_pricing":{"type":"string","enum":["hourly","flat"],"description":"hourly: the customer pays for the time worked, at the labor item's price. flat: the customer pays the template's charge lines whatever the time; time is recorded for cost and warranty only, and the parts it includes are used at no charge."},"labor_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item hourly time on these jobs is charged as. NULL uses the business's (tenant_policy.service_labor_item_id)."},"labor_item_sku":{"description":"The labor item's SKU.","type":["string","null"]},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The standard time for the whole job."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service checklist the technician answers on these jobs."},"checklist_name":{"description":"The checklist's name.","type":["string","null"]},"checklist_required":{"type":"boolean","description":"True when a job from this template is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Needs checklist_id."},"is_active":{"type":"boolean","description":"False once the business no longer does this kind of job. Jobs already opened from it keep what was copied."},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where it comes in the job, smallest first."},"name":{"type":"string","description":"What the step is."},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The standard time for the step."},"billing":{"anyOf":[{"type":"string","enum":["customer","warranty","included","no_charge"]},{"type":"null"}],"description":"Who pays for time on this step by default: customer, warranty, included or no_charge. NULL: the job decides (warranty work: warranty; a flat-priced job: included; otherwise customer)."}},"required":["id","position","name","estimated_minutes","billing"],"additionalProperties":false},"description":"Its steps in use, in order."},"lines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item. Fixed."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"role":{"type":"string","enum":["charge","included_part","suggested_part"],"description":"charge: a service charged on every job of this kind (its flat price, a service-call fee), put on the job's charges when it is opened and handed over when a visit is completed. included_part: a part the flat price covers, up to quantity per job, used at no extra charge. suggested_part: a part the job usually needs, put on the technician's list of parts to bring and charged when used. A charge is a service; the parts are goods."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many, in the item's unit: charged, included, or to bring. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["id","item_id","sku","item_name","role","quantity"],"additionalProperties":false},"description":"What it charges, includes and usually needs, in use."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","code","name","description","problem_text","labor_pricing","labor_item_id","labor_item_sku","estimated_minutes","checklist_id","checklist_name","checklist_required","is_active","tasks","lines","created_at","updated_at"],"additionalProperties":false,"description":"A kind of job the business does again and again, such as a spa winterize or a heater replacement: its steps with their standard times, the parts it uses, how its labor is priced, and the checklist the technician answers. Opening a work order from it copies all of this onto the job, so a later change to the template never changes a job already open."},"JobTemplatePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/JobTemplate"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of kinds of job."},"LaborEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"work_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The job the time was spent on. Taken from the visit when only the visit is named. NULL only for a visit with no job (a care-plan visit), whose time is included or no charge."},"work_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"appointment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The visit the time was spent on, if any: completing it charges its time."},"appointment_scheduled_start":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When that visit was booked for, or null."},"work_order_task_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The step of the job the time was spent on, if any."},"work_order_task_name":{"description":"That step's name.","type":["string","null"]},"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who spent the time. Fixed."},"staff_member_name":{"type":"string","description":"Whose time, by name."},"activity":{"type":"string","enum":["work","travel"],"description":"work: on the job. travel: getting to it. Fixed."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays for the time. customer: charged by the hour at the labor item's price. warranty: charged at 0 and claimed from the manufacturer; only on warranty work with a warranty in force on the day, never for travel. included: covered by the job's flat price (or, with no job, a care plan). no_charge: given away. Defaults to the step's billing, travel to tenant_policy.travel_time_billing."},"started_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the timer was started, by the technician's device. NULL for time entered as a duration."},"ended_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the timer was stopped, by the device. NULL while it is running, and for time entered as a duration. At most 24 hours after started_at."},"is_running":{"type":"boolean","description":"True while the timer runs: started and not stopped."},"worked_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the work, at the job's location: worked out from started_at, and sent with a duration (today by default)."},"minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How long, from 1 to 1440: worked out from start and stop to the minute, or sent as a duration. NULL while the timer runs."},"notes":{"description":"What was done in the time.","type":["string","null"]},"is_void":{"type":"boolean","description":"True once the entry was withdrawn as recorded in error, with void_reason. Time already charged cannot be voided."},"void_reason":{"description":"Why it was voided.","type":["string","null"]},"billed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the system gathered the time into charges, or settled it as included or no charge: when its visit or job was completed. From then the entry is fixed. Set by the system."},"sales_order_line_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The charge line this time was gathered into: a labor line at the item's price for customer time, or at 0 for warranty time. Set by the system."},"line_hours":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The hours its labor line charges now, for every entry on that line together. Null with no line."},"line_hours_taken_back":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The hours taken back off its labor line (returnSalesOrderGoods), for every entry on that line together: the line once charged line_hours plus these. Null with no line."},"said":{"description":"What recording it did beyond the entry itself, in words: a running timer it stopped. Null otherwise.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","work_order_id","work_order_number","appointment_id","appointment_scheduled_start","work_order_task_id","work_order_task_name","staff_member_id","staff_member_name","activity","billing","started_at","ended_at","is_running","worked_on","minutes","notes","is_void","void_reason","billed_at","sales_order_line_id","line_hours","line_hours_taken_back","said","created_at","next"],"additionalProperties":false,"description":"Time one technician spent on one job: travelling to it or working on it, timed with a start and a stop or entered as a duration. It is what the customer is charged for by the hour, what warranty labor is claimed from, and what the job cost in labor. Payroll is not kept here."},"LaborEntryPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LaborEntry"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of time."},"ServiceVisitResult":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The visit."},"appointment_status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"scheduled: planned. completed: it happened. cancelled: it will not happen; make a new appointment to try again. failed: the crew went but the visit did not do its job (a delivery refused or not possible); book again from it. Completed, cancelled and failed are final. To move a scheduled appointment, change its times."},"charged":{"type":"string","description":"What the job's charges come to now, in words: per order, what is charged, paid and still to pay."},"work_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The job."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["appointment_id","appointment_status","charged","work_order_id","work_order_number","next"],"additionalProperties":false,"description":"A service visit as finished, and what its job's charges came to."},"ServiceVisitCharges":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The visit."},"appointment_status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"The visit's status now: pricing does not change it."},"work_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The job."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"finished_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"The moment priced: when the running timers would stop."},"orders":{"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The sales order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"lines":{"type":"array","items":{"type":"object","properties":{"description":{"type":"string","description":"What the line says on the order. Defaults to the item's name."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many the customer is buying, in the item's unit. 0 drops the line. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. A decimal string with up to 2 decimal places, for example \"12.50\"."},"extended_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the line comes to before tax: quantity times unit_price, or for a line sold by the pack quantity times pack_price over pack_size (packs times pack_price, for whole packs), less discount_amount. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the line, the sum of its sales_order_line_tax rows. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"kind":{"type":"string","enum":["part","labor","service"],"description":"part: a part used on the job. labor: time on it, quantity in hours. service: anything else, such as a service-call fee."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays for it: the customer, or the manufacturer's warranty (at 0 to the customer)."}},"required":["description","quantity","unit_price","extended_price","tax_amount","kind","billing"],"additionalProperties":false},"description":"What it charges, in line order."},"subtotal":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sum of the lines before tax, after discounts. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax on the order. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer has paid so far, less refunds. Calculated. Paid beyond fulfilled is a deposit; fulfilled beyond paid is owed. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["sales_order_id","order_number","lines","subtotal","tax_total","total","paid_total"],"additionalProperties":false},"description":"The job's orders that are not cancelled, oldest first, as they would stand."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the job's charges would come to, tax included: what the customer signs for. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What has been paid toward them already. A decimal string with up to 2 decimal places, for example \"12.50\"."},"to_pay":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What would be left to pay: total less paid_total, or 0. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["appointment_id","appointment_status","work_order_id","work_order_number","finished_at","orders","total","paid_total","to_pay"],"additionalProperties":false,"description":"What finishing a service visit would charge, priced by the same rules and then not kept: what the customer is shown before they sign."},"TechnicianDay":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The technician."},"staff_member_name":{"type":"string","description":"Their name."},"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business day, at their home location (or the tenant's first)."},"van_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The van they drive, if any."},"van_location_branch_code":{"description":"Its branch code.","type":["string","null"]},"running_timer":{"anyOf":[{"$ref":"#/components/schemas/LaborEntry"},{"type":"null"}],"description":"Their timer still running, if any."},"visits":{"type":"array","items":{"type":"object","properties":{"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The visit."},"appointment_status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"Its status."},"kind":{"type":"string","enum":["delivery","service","consultation"],"description":"delivery: goods are brought and installed. service: a technician works on equipment. consultation: a meeting to advise or assess, with nothing delivered; one naming a sales_order_id is a site check before that order's delivery."},"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"scheduled_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"End of the window the customer was given."},"en_route_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew set off for it. Set once, while it is scheduled; the customer is told. The window cannot move after it."},"arrived_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew arrived, by their device. Set once, while it is scheduled, and not before en_route_at."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_address":{"description":"Where, in one line.","type":["string","null"]},"instructions":{"description":"What the crew needs to know beforehand: access, equipment to bring, what was promised.","type":["string","null"]},"equipment_name":{"description":"The equipment worked on.","type":["string","null"]},"serial_number":{"description":"Its serial number, when known.","type":["string","null"]},"warranties_in_force":{"type":"array","items":{"type":"string"},"description":"Warranties on it in force today, in words: name, who stands behind it, last day."},"work_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The job, if the visit is for one."},"work_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"problem":{"description":"What the customer reported or asked for, in their terms.","type":["string","null"]},"diagnosis":{"description":"What the technician found: the cause, readings, error codes. The customer's words stay in problem, what was done goes in resolution. A warranty claim's technician notes start from it.","type":["string","null"]},"job_template_code":{"description":"The kind of job, by code.","type":["string","null"]},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where it comes in the job, smallest first."},"name":{"type":"string","description":"What the step is."},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The standard time for the step."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays, by default, for time and parts on this step. customer: charged at the business's prices. warranty: claimed from the manufacturer, at 0 to the customer; only on warranty work. included: covered by the job's flat price; only on a flat-priced job. no_charge: given away, such as a callback or goodwill."},"status":{"type":"string","enum":["open","done","skipped"],"description":"open: still to do. done: finished (done_at). skipped: not needed after all."},"done_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was finished, by the technician's device. Defaults to now when it is marked done."},"notes":{"description":"What the technician noted on the step.","type":["string","null"]},"minutes_recorded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Time recorded on the step so far, voided time left out."}},"required":["id","position","name","estimated_minutes","billing","status","done_at","notes","minutes_recorded"],"additionalProperties":false},"description":"The job's steps."},"checklist_name":{"description":"The job's checklist, if any.","type":["string","null"]},"checklist_answered":{"type":"boolean","description":"True once a check on the job's checklist is recorded."}},"required":["appointment_id","appointment_status","kind","scheduled_start","scheduled_end","en_route_at","arrived_at","customer_id","customer_name","site_address","instructions","equipment_name","serial_number","warranties_in_force","work_order_id","work_order_number","problem","diagnosis","job_template_code","tasks","checklist_name","checklist_answered"],"additionalProperties":false},"description":"Their visits that day, in start order."},"parts_to_bring":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The part."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"needed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many the day's jobs are expected to use, less what they used. A decimal string with up to 3 decimal places, for example \"12.500\"."},"on_van":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many their van holds now. A decimal string with up to 3 decimal places, for example \"12.500\"."},"short":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many more to load: needed less on_van, at least 0. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","sku","item_name","needed","on_van","short"],"additionalProperties":false},"description":"The included and suggested parts of the day's open jobs against what the van holds."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["staff_member_id","staff_member_name","date","van_location_id","van_location_branch_code","running_timer","visits","parts_to_bring","next"],"additionalProperties":false,"description":"One technician's day: where to go, what to do and what to bring."},"LaborCostRate":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The technician. Fixed."},"staff_member_name":{"type":"string","description":"The technician's name."},"effective_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day the rate applies to. It applies until the next rate's day. Fixed."},"cost_rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one hour costs the business. A decimal string with up to 2 decimal places, for example \"12.50\"."},"notes":{"description":"How it was worked out, in words.","type":["string","null"]},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","staff_member_id","staff_member_name","effective_on","cost_rate","notes","updated_at"],"additionalProperties":false,"description":"What an hour of a technician's time costs the business, from a date: wages plus burden, as the business works it out. Used only to report what jobs cost and earned, never posted: wages are posted by payroll in the accounting system. A correction restates the reports, which is wanted, since nothing was posted."},"LaborCostRates":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The technician."},"staff_member_name":{"type":"string","description":"Their name."},"rates":{"type":"array","items":{"$ref":"#/components/schemas/LaborCostRate"},"description":"Their rates, newest first."}},"required":["staff_member_id","staff_member_name","rates"],"additionalProperties":false,"description":"A technician's cost rates over time."},"ServiceMargin":{"type":"object","properties":{"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First day, by the day each job was completed."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day."},"group_by":{"type":"string","enum":["work_order","technician","template"],"description":"What each row is."},"rows":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"The work order number, the technician's name, or the template code (none)."},"jobs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Completed jobs counted."},"revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue its charges recognized, before tax, fees kept included. A decimal string with up to 2 decimal places, for example \"12.50\"."},"parts_cost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the goods handed over on its charges cost. A decimal string with up to 2 decimal places, for example \"12.50\"."},"labor_cost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What its recorded time cost, at cost rates. A decimal string with up to 2 decimal places, for example \"12.50\"."},"warranty_recovered":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What manufacturers approved, or paid where a claim is paid, on its claims. A decimal string with up to 2 decimal places, for example \"12.50\"."},"margin":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue and warranty recovered, less parts and labor cost. A decimal string with up to 2 decimal places, for example \"12.50\"."},"labor_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Time recorded."},"estimated_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Standard time of the jobs that have one."},"labor_rate_missing":{"type":"boolean","description":"True when some time had no cost rate, so labor_cost is short."}},"required":["key","jobs","revenue","parts_cost","labor_cost","warranty_recovered","margin","labor_minutes","estimated_minutes","labor_rate_missing"],"additionalProperties":false},"description":"One row per group, largest revenue first."}},"required":["from","to","group_by","rows"],"additionalProperties":false,"description":"What completed jobs earned and cost: revenue and parts cost as posted, labor at cost rates (not posted), and warranty recovered."},"EquipmentWarranty":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What is covered, for example \"Shell structure\" or \"Parts and labor\"."},"provider":{"type":"string","enum":["manufacturer","dealer"],"description":"Who stands behind it: manufacturer or dealer (the tenant itself)."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manufacturer: required when provider is manufacturer, and NULL when the dealer stands behind it. Claims go to this vendor."},"vendor_name":{"description":"The manufacturer's name, or null for the dealer's own warranty.","type":["string","null"]},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"First day of cover, usually the delivery date."},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day of cover."},"terms":{"description":"What the warranty covers and excludes, in words.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the warranty is void, for example after the equipment changed hands."},"in_force_today":{"type":"boolean","description":"True when the warranty is active and today is between its first and last day."}},"required":["id","name","provider","vendor_id","vendor_name","starts_on","ends_on","terms","is_active","in_force_today"],"additionalProperties":false,"description":"One warranty covering one piece of equipment for a period. A hot tub usually has several with different terms: shell, plumbing, parts and labor. A work order is warranty work when a warranty is in force on its date."},"Equipment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who owns it. Fixed once the equipment exists: work orders, subscriptions and module records name equipment together with its owner. When equipment changes hands, retire this row and record it anew for the new owner."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is installed: one of the owner's own sites. NULL until known, for example a unit the customer carried away."},"site_name":{"description":"The site's name, or null when it is not known where it is.","type":["string","null"]},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The serialized unit this is, when the tenant sold it. Set by the system when the unit is handed over, never sent. NULL for equipment bought elsewhere."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The catalog item this is a unit of, when the tenant carries it."},"sku":{"description":"The catalog item's SKU, when the tenant carries it.","type":["string","null"]},"name":{"type":"string","description":"What to call it, for example \"Grandee hot tub\"."},"brand":{"description":"The brand, for equipment not in the tenant's catalog.","type":["string","null"]},"model":{"description":"The model, for equipment not in the tenant's catalog.","type":["string","null"]},"serial_number":{"description":"The serial number, for equipment not in the tenant's stock records. For a unit the tenant sold, its serial number from stock.","type":["string","null"]},"installed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date it was delivered or installed."},"removed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date it was returned, removed or replaced."},"notes":{"description":"Free-form notes for staff, for example where the breaker is.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the customer no longer has it. Its service history stays."},"warranties":{"type":"array","items":{"$ref":"#/components/schemas/EquipmentWarranty"},"description":"Its warranties, latest ending first."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","customer_id","customer_name","site_id","site_name","serialized_unit_id","item_id","sku","name","brand","model","serial_number","installed_on","removed_on","notes","is_active","warranties","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"One unit a customer owns that the tenant may service: a hot tub, a swim spa, a sauna. Created automatically when a serialized unit is handed to a customer, and by hand for equipment bought elsewhere. Work orders, warranties and recurring plans point at it. schema.org: IndividualProduct, as owned."},"EquipmentPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Equipment"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of equipment."},"WorkOrderSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["open","completed","cancelled"],"description":"open: still to do. completed: done, with the resolution recorded. cancelled: not going to be done. Completed and cancelled are final."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who the work is for. Fixed: its visits and charges are that customer's."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the work is done: one of the customer's own sites. NULL for work done at the location, for example on a unit brought in."},"site_name":{"description":"The site's name, or null for work done at the location.","type":["string","null"]},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment being worked on, which must be the customer's own. On warranty work its warranties decide what is free, so changing it then needs warranty.manage, and is refused once parts or time are recorded under it."},"equipment_name":{"description":"The equipment's name, when the work is on equipment.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location responsible for the work, whose technicians and stock are used. Moving the job to another needs labor.manage at both."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The latest sales order carrying this job's parts and labor. NULL until there is something to charge. getWorkOrder lists them all in charge_orders."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The latest charges order's number, if there is one."},"is_warranty":{"type":"boolean","description":"True when the work is covered by a warranty, so the customer is not charged for the covered part. Setting or clearing it needs warranty.manage at the job's location, and it is fixed once parts not billed to the customer, or time billed to warranty, are recorded on the job."},"warranty_in_force":{"type":"boolean","description":"True when a warranty on the equipment is in force today. Set is_warranty when it covers this work."},"warranties_in_force":{"type":"array","items":{"type":"object","properties":{"equipment_warranty_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The warranty: what createWarrantyClaim's equipment_warranty_id takes."},"equipment_warranty_name":{"type":"string","description":"What is covered, for example \"Shell structure\" or \"Parts and labor\"."},"provider":{"type":"string","enum":["manufacturer","dealer"],"description":"Who stands behind it: manufacturer or dealer (the tenant itself)."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manufacturer: required when provider is manufacturer, and NULL when the dealer stands behind it. Claims go to this vendor."},"vendor_name":{"description":"The manufacturer's name.","type":["string","null"]},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day of cover."}},"required":["equipment_warranty_id","equipment_warranty_name","provider","vendor_id","vendor_name","ends_on"],"additionalProperties":false},"description":"The warranties on the equipment in force today at the job's location, ending soonest first."},"problem":{"type":"string","description":"What the customer reported or asked for, in their terms."},"diagnosis":{"description":"What the technician found: the cause, readings, error codes. The customer's words stay in problem, what was done goes in resolution. A warranty claim's technician notes start from it.","type":["string","null"]},"resolution":{"description":"What was found and what was done. Required to complete the work order.","type":["string","null"]},"job_template_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The kind of job it was opened as, whose tasks, estimate, checklist, pricing and charges were copied onto it then. A later change to the template never changes the job. Fixed."},"job_template_code":{"description":"The kind of job, by code.","type":["string","null"]},"labor_pricing":{"type":"string","enum":["hourly","flat"],"description":"hourly: the customer pays for the time worked, at the labor item's price. flat: the customer pays the job's set charges whatever the time; time is recorded for cost and warranty only, and parts the flat price includes are used at no charge. Taken from the template, and fixed."},"labor_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item hourly customer and warranty time on this job is charged as: a service sold by the hour, in fractions. NULL uses the business's (tenant_policy.service_labor_item_id). One priced below the template's or the business's needs order.discount at the job's location."},"labor_item_sku":{"description":"The labor item's SKU.","type":["string","null"]},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The standard time for the whole job, from its template or as estimated. Compared with the time recorded."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service checklist the technician answers on the job, such as amperage readings and a leak check."},"checklist_name":{"description":"The checklist's name.","type":["string","null"]},"checklist_required":{"type":"boolean","description":"True when the job is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Dropping it, or answering another checklist instead, needs labor.manage."},"completed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the work order was completed. Set automatically."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","work_order_number","status","customer_id","customer_name","site_id","site_name","equipment_id","equipment_name","location_id","location_name","sales_order_id","order_number","is_warranty","warranty_in_force","warranties_in_force","problem","diagnosis","resolution","job_template_id","job_template_code","labor_pricing","labor_item_id","labor_item_sku","estimated_minutes","checklist_id","checklist_name","checklist_required","completed_at","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it. As listed: getWorkOrder adds visits and claims."},"WorkOrder":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["open","completed","cancelled"],"description":"open: still to do. completed: done, with the resolution recorded. cancelled: not going to be done. Completed and cancelled are final."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who the work is for. Fixed: its visits and charges are that customer's."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the work is done: one of the customer's own sites. NULL for work done at the location, for example on a unit brought in."},"site_name":{"description":"The site's name, or null for work done at the location.","type":["string","null"]},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment being worked on, which must be the customer's own. On warranty work its warranties decide what is free, so changing it then needs warranty.manage, and is refused once parts or time are recorded under it."},"equipment_name":{"description":"The equipment's name, when the work is on equipment.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location responsible for the work, whose technicians and stock are used. Moving the job to another needs labor.manage at both."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The latest sales order carrying this job's parts and labor. NULL until there is something to charge. getWorkOrder lists them all in charge_orders."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The latest charges order's number, if there is one."},"is_warranty":{"type":"boolean","description":"True when the work is covered by a warranty, so the customer is not charged for the covered part. Setting or clearing it needs warranty.manage at the job's location, and it is fixed once parts not billed to the customer, or time billed to warranty, are recorded on the job."},"warranty_in_force":{"type":"boolean","description":"True when a warranty on the equipment is in force today. Set is_warranty when it covers this work."},"warranties_in_force":{"type":"array","items":{"type":"object","properties":{"equipment_warranty_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The warranty: what createWarrantyClaim's equipment_warranty_id takes."},"equipment_warranty_name":{"type":"string","description":"What is covered, for example \"Shell structure\" or \"Parts and labor\"."},"provider":{"type":"string","enum":["manufacturer","dealer"],"description":"Who stands behind it: manufacturer or dealer (the tenant itself)."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The manufacturer: required when provider is manufacturer, and NULL when the dealer stands behind it. Claims go to this vendor."},"vendor_name":{"description":"The manufacturer's name.","type":["string","null"]},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Last day of cover."}},"required":["equipment_warranty_id","equipment_warranty_name","provider","vendor_id","vendor_name","ends_on"],"additionalProperties":false},"description":"The warranties on the equipment in force today at the job's location, ending soonest first."},"problem":{"type":"string","description":"What the customer reported or asked for, in their terms."},"diagnosis":{"description":"What the technician found: the cause, readings, error codes. The customer's words stay in problem, what was done goes in resolution. A warranty claim's technician notes start from it.","type":["string","null"]},"resolution":{"description":"What was found and what was done. Required to complete the work order.","type":["string","null"]},"job_template_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The kind of job it was opened as, whose tasks, estimate, checklist, pricing and charges were copied onto it then. A later change to the template never changes the job. Fixed."},"job_template_code":{"description":"The kind of job, by code.","type":["string","null"]},"labor_pricing":{"type":"string","enum":["hourly","flat"],"description":"hourly: the customer pays for the time worked, at the labor item's price. flat: the customer pays the job's set charges whatever the time; time is recorded for cost and warranty only, and parts the flat price includes are used at no charge. Taken from the template, and fixed."},"labor_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item hourly customer and warranty time on this job is charged as: a service sold by the hour, in fractions. NULL uses the business's (tenant_policy.service_labor_item_id). One priced below the template's or the business's needs order.discount at the job's location."},"labor_item_sku":{"description":"The labor item's SKU.","type":["string","null"]},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The standard time for the whole job, from its template or as estimated. Compared with the time recorded."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service checklist the technician answers on the job, such as amperage readings and a leak check."},"checklist_name":{"description":"The checklist's name.","type":["string","null"]},"checklist_required":{"type":"boolean","description":"True when the job is not completed until its checklist has been recorded with every required question answered, passed or failed: a failed check is the record of what was found. Dropping it, or answering another checklist instead, needs labor.manage."},"completed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the work order was completed. Set automatically."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"appointments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["delivery","service","consultation"],"description":"delivery: goods are brought and installed. service: a technician works on equipment. consultation: a meeting to advise or assess, with nothing delivered; one naming a sales_order_id is a site check before that order's delivery."},"status":{"type":"string","enum":["scheduled","completed","cancelled"],"description":"scheduled: planned. completed: it happened. cancelled: it will not happen; make a new appointment to try again. failed: the crew went but the visit did not do its job (a delivery refused or not possible); book again from it. Completed, cancelled and failed are final. To move a scheduled appointment, change its times."},"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."}},"required":["id","kind","status","scheduled_start"],"additionalProperties":false},"description":"The visits for this job, earliest first."},"charge_orders":{"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The sales order."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."},"total":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated."}},"required":["sales_order_id","order_number","status","total"],"additionalProperties":false},"description":"Every sales order carrying this job's parts and labor, oldest first. New charges go on the one still open, or on a new one."},"claims":{"type":"array","items":{"$ref":"#/components/schemas/WarrantyClaim"},"description":"Warranty claims made for this work."},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where it comes in the job, smallest first."},"name":{"type":"string","description":"What the step is."},"estimated_minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The standard time for the step."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays, by default, for time and parts on this step. customer: charged at the business's prices. warranty: claimed from the manufacturer, at 0 to the customer; only on warranty work. included: covered by the job's flat price; only on a flat-priced job. no_charge: given away, such as a callback or goodwill."},"status":{"type":"string","enum":["open","done","skipped"],"description":"open: still to do. done: finished (done_at). skipped: not needed after all."},"done_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was finished, by the technician's device. Defaults to now when it is marked done."},"notes":{"description":"What the technician noted on the step.","type":["string","null"]},"minutes_recorded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Time recorded on the step so far, voided time left out."}},"required":["id","position","name","estimated_minutes","billing","status","done_at","notes","minutes_recorded"],"additionalProperties":false},"description":"The job's steps, in order."},"labor_entries":{"type":"array","items":{"$ref":"#/components/schemas/LaborEntry"},"description":"Time recorded on the job, oldest first, voided time included."},"labor_minutes":{"type":"object","properties":{"customer":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutes billed to the customer."},"warranty":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutes billed to warranty."},"included":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutes included in a flat price or care plan."},"no_charge":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutes given away."}},"required":["customer","warranty","included","no_charge"],"additionalProperties":false,"description":"Stopped time recorded on the job, voided time left out, by who pays for it."},"parts_used":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The part: a good. Taken from the use it reverses on a taking-back."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many, in the item's unit: positive when used, negative only on a row that takes back an earlier one, and never more than it used. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_taken_back":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much of it was taken back as recorded in error. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_used":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much was used in the end: quantity less quantity_taken_back. A decimal string with up to 3 decimal places, for example \"12.500\"."},"billing":{"type":"string","enum":["customer","warranty","included","no_charge"],"description":"Who pays for it. customer: at unit_price. warranty: at 0, claimed from the manufacturer; only on warranty work with a warranty in force on the day. included: at 0, covered by the job's flat price up to what it includes. no_charge: at 0, given away. A warranty job takes neither included nor no-charge parts: every part at 0 on it is claimed. Defaults to the step's billing, else warranty on warranty work, included on a flat-priced job while the price includes more of it, else customer."},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one was charged, before tax: for a customer part the item's price unless another is sent (below it needs the same discount allowance as any sale); 0 for the others. A decimal string with up to 2 decimal places, for example \"12.50\"."},"from_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the part came from: by default, for a job at a customer's site, the van the technician drives; otherwise the job's location. Anywhere but the job's location or the caller's own van needs stock.transfer there. A part taken back goes back there; once that is retired, to the van's home, or else the job's location."},"from_location_branch_code":{"type":"string","description":"Where it came from, by branch code."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The charges order it is on."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"used_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it was used, by the technician's device. Defaults to now."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who used it, or took it back."},"staff_member_name":{"description":"Who used it, by name.","type":["string","null"]},"failed_part_number":{"description":"For a warranty part: the part number of the part taken out, for the claim.","type":["string","null"]},"failed_serial_number":{"description":"For a warranty part: the serial number of the part taken out.","type":["string","null"]},"failed_part_description":{"description":"For a warranty part: what failed, in words.","type":["string","null"]},"notes":{"description":"Anything worth recording about it.","type":["string","null"]}},"required":["id","item_id","sku","item_name","quantity","quantity_taken_back","quantity_used","billing","unit_price","from_location_id","from_location_branch_code","sales_order_id","order_number","used_at","staff_member_id","staff_member_name","failed_part_number","failed_serial_number","failed_part_description","notes"],"additionalProperties":false},"description":"Parts used on the job, oldest first, each with how much of it was taken back."},"expected_parts":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The part."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many the flat price includes, or how many to bring. A decimal string with up to 3 decimal places, for example \"12.500\"."},"is_included":{"type":"boolean","description":"True when the job's flat price covers up to quantity of it; false when it is only a suggestion."},"quantity_used":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much of it the job has used so far, taken-back parts left out. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","sku","item_name","quantity","is_included","quantity_used"],"additionalProperties":false},"description":"The parts its kind of job includes in its flat price, or suggests bringing, with how much is used."},"photos":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"file_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file: read it with getFile."},"shows":{"anyOf":[{"type":"string","enum":["fault","repair","serial_plate","site","other"]},{"type":"null"}],"description":"For a job's photo: what it shows. fault: the failure as found. repair: the work as left. serial_plate: the equipment's serial plate. site: the site. other. Manufacturers ask for the fault and the serial plate with a warranty claim."},"caption":{"description":"What it shows, for example \"Pad from the street side\".","type":["string","null"]},"taken_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was taken, by the device: it may be before it was uploaded, never later than now."},"is_active":{"type":"boolean","description":"False once withdrawn as taken in error. Only a photo or document of a visit still scheduled, or of a job still open, can be withdrawn, never a signature or a check's photo."}},"required":["id","file_id","shows","caption","taken_at","is_active"],"additionalProperties":false},"description":"Photos taken on the job, oldest first."},"checks":{"type":"array","items":{"type":"object","properties":{"checklist_response_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The check: read it with getSiteCheck."},"check_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, assigned automatically. Of two site checks of one site for one order, the higher number is the one that counts."},"checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The checklist answered. NULL for a check recorded as a verdict with notes and photos, with no checklist."},"checklist_name":{"description":"That checklist's name.","type":["string","null"]},"outcome":{"type":"string","enum":["passed","failed"],"description":"passed or failed. Passed only when every required question is answered, every answer passes and each photo question has its photos: checked when the transaction commits."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["checklist_response_id","check_number","checklist_id","checklist_name","outcome","created_at"],"additionalProperties":false},"description":"Service checklists answered on the job, oldest first."},"sign_offs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["authorization","completion"],"description":"authorization: the customer agreed to the work before it was done. completion: they accepted it as done, and its charges."},"statement":{"type":"string","description":"The statement they signed under, as the business's policy worded it at the time (tenant_policy.service_authorization_statement or service_completion_statement). Set by the system."},"amount_authorized":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For an authorization: the most the customer agreed to, before tax. NULL authorizes the charges shown. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For an authorization: the most the customer agreed to, before tax. NULL authorizes the charges shown."},"charges_total_shown":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the job's charges came to, tax included, when it was signed: the total of its charges orders not cancelled. Set by the system. A decimal string with up to 2 decimal places, for example \"12.50\"."},"signed_by_name":{"description":"The name of the person who signed, as they gave it. Given with the signature.","type":["string","null"]},"signed_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When they signed, by the technician's device, or when it was recorded with nobody there. Defaults to now."},"customer_absent":{"type":"boolean","description":"True for a completion recorded with nobody there to sign. Needs a photo on the job, and labor.manage where the business requires a signature."}},"required":["id","kind","statement","amount_authorized","charges_total_shown","signed_by_name","signed_at","customer_absent"],"additionalProperties":false},"description":"The customer's signatures on the job, oldest first."},"economics":{"anyOf":[{"type":"object","properties":{"revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue its charges recognized, before tax, fees kept included. A decimal string with up to 2 decimal places, for example \"12.50\"."},"parts_cost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the goods handed over on its charges cost. A decimal string with up to 2 decimal places, for example \"12.50\"."},"labor_cost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What its recorded time cost, at cost rates. A decimal string with up to 2 decimal places, for example \"12.50\"."},"labor_rate_missing":{"type":"boolean","description":"True when some of its time had no cost rate, so labor_cost is short."},"warranty_recovered":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What manufacturers approved, or paid where a claim is paid, on its claims. A decimal string with up to 2 decimal places, for example \"12.50\"."},"margin":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue and warranty recovered, less parts and labor cost. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["revenue","parts_cost","labor_cost","labor_rate_missing","warranty_recovered","margin"],"additionalProperties":false},{"type":"null"}],"description":"What the job earned and cost. Null without cost.view."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","work_order_number","status","customer_id","customer_name","site_id","site_name","equipment_id","equipment_name","location_id","location_name","sales_order_id","order_number","is_warranty","warranty_in_force","warranties_in_force","problem","diagnosis","resolution","job_template_id","job_template_code","labor_pricing","labor_item_id","labor_item_sku","estimated_minutes","checklist_id","checklist_name","checklist_required","completed_at","custom_fields","created_at","updated_at","appointments","charge_orders","claims","tasks","labor_entries","labor_minutes","parts_used","expected_parts","photos","checks","sign_offs","economics","next"],"additionalProperties":false,"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it."},"WorkOrderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkOrderSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of work orders."},"RouteStop":{"type":"object","properties":{"position":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where the stop comes on this person's day, 1 first: the order they go in, as read now."},"in_order":{"type":"boolean","description":"True when the dispatcher put it in this place. False for a visit never put in order (booked since, or moved here from another day or person): it comes after those that were, by its window."},"appointment_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The visit: see all of it with getAppointment."},"appointment_status":{"type":"string","enum":["scheduled","completed","cancelled","failed"],"description":"scheduled: planned. completed: it happened. cancelled: it will not happen; make a new appointment to try again. failed: the crew went but the visit did not do its job (a delivery refused or not possible); book again from it. Completed, cancelled and failed are final. To move a scheduled appointment, change its times."},"kind":{"type":"string","enum":["delivery","service","consultation"],"description":"delivery: goods are brought and installed. service: a technician works on equipment. consultation: a meeting to advise or assess, with nothing delivered; one naming a sales_order_id is a site check before that order's delivery."},"scheduled_start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"Start of the window the customer was given."},"scheduled_end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"End of the window the customer was given."},"en_route_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew set off for it. Set once, while it is scheduled; the customer is told. The window cannot move after it."},"arrived_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the crew arrived, by their device. Set once, while it is scheduled, and not before en_route_at."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who the visit is with."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_telephone":{"description":"The customer's phone number to call about the visit: their primary active one, in E.164 form.","type":["string","null"]},"site_address":{"description":"Where the stop is, on one line, for the crew. Null when the customer comes to the store.","type":["string","null"]},"postal_code":{"description":"ZIP or postal code.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location responsible: its crew goes, and a delivery leaves from its stock. The sale itself still belongs to the location that made it."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"work_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The work order the visit is for, when it is a service call. It must be the same customer's, and still open."},"work_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The work order's number, for a service call."},"order_numbers":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"For a delivery: the orders whose goods it hands over. Empty for other visits."},"others_going":{"type":"array","items":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Someone else on the visit."},"staff_member_name":{"type":"string","description":"Their name."},"is_lead":{"type":"boolean","description":"True for the person in charge of the visit: at most one. A completed delivery is recorded as handed over by the lead."}},"required":["staff_member_id","staff_member_name","is_lead"],"additionalProperties":false},"description":"Who else goes on it: the rest of a delivery crew. Empty when the person goes alone."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["position","in_order","appointment_id","appointment_status","kind","scheduled_start","scheduled_end","en_route_at","arrived_at","customer_id","customer_name","customer_telephone","site_address","postal_code","location_id","location_name","work_order_id","work_order_number","order_numbers","others_going","next"],"additionalProperties":false,"description":"One visit on a person's day, where it comes in their order."},"Route":{"type":"object","properties":{"staff_member_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Whose day it is."},"staff_member_name":{"type":"string","description":"Their name."},"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day, at the stores the visits are booked from."},"van_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The van they drive (a location of kind vehicle), whose parts their jobs use. Null for none."},"van_location_branch_code":{"description":"The van's branch code.","type":["string","null"]},"time_off_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Time off they have on the day: they are out, so their stops usually go to someone else."},"time_off_reason":{"description":"Why, in a few words, as the business wants it shown to whoever books: \"Sick\", \"Vacation\". Optional.","type":["string","null"]},"stops":{"type":"array","items":{"$ref":"#/components/schemas/RouteStop"},"description":"Every visit they go on that day, cancelled ones left out, in the order they go: those put in order first, then the rest by window."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["staff_member_id","staff_member_name","date","van_location_id","van_location_branch_code","time_off_id","time_off_reason","stops","next"],"additionalProperties":false,"description":"One person's visits on one day, in the order they go."},"DayRoutes":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store whose day it is."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day, at the store."},"time_zone":{"type":"string","description":"IANA time zone, for example America/Boise. Business days and opening hours are evaluated in it."},"routes":{"type":"array","items":{"$ref":"#/components/schemas/Route"},"description":"The day of each person going on a visit booked from the store that day, by name. A day holds all of the person's visits, those booked from other stores too."},"unassigned":{"type":"array","items":{"$ref":"#/components/schemas/RouteStop"},"description":"Visits booked from the store that day with nobody on them yet, by window: give each to someone with moveRouteStop."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["location_id","location_name","date","time_zone","routes","unassigned","next"],"additionalProperties":false,"description":"A store's day of visits as routes: each person's stops in the order they go."},"FloorPlanLender":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The lender's name, unique within the business."},"interest_rate_percent":{"type":"string","description":"The yearly interest rate, as a percent, charged on what is owed on each unit after its free days. Used to estimate interest accrued; interest is booked as the lender bills it."},"free_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days after a unit is financed before interest starts (free flooring). 0 for none."},"curtailments":{"type":"array","items":{"type":"object","properties":{"after_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days after a unit is financed when this part falls due."},"percent":{"type":"string","description":"The percent of the amount financed due then."}},"required":["after_days","percent"],"additionalProperties":false},"description":"The curtailment schedule, earliest first. Empty when the lender asks for none."},"payoff_due_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days after a unit is sold by which what is left on it must be paid off. 0 for the day it is sold."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender's Floor Plan Payable account: what the business owes this lender. Made with the lender, a control account the system posts to. Set by the system."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"units_open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of the lender's units still owe it something."},"balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the business owes the lender now, over all its units. Null without cost.view."},"notes":{"description":"Free-form notes for staff: account numbers, contacts, audit dates.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the business no longer floors units with this lender. Retired only when none of its units still owe it anything."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","interest_rate_percent","free_days","curtailments","payoff_due_days","ledger_account_id","ledger_account_code","ledger_account_name","units_open","balance","notes","is_active","created_at","updated_at"],"additionalProperties":false,"description":"A lender that finances units on the floor (a floor-plan line of credit): who it is and its terms. Units it finances are floor_plan_loan rows. Each lender has its own Floor Plan Payable account."},"FloorPlanLenderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FloorPlanLender"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of floor-plan lenders and the cursor for the next."},"FloorPlanPayment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"principal_amount":{"type":"string","description":"What the payment takes off what is owed on the unit: a curtailment, or the payoff when it clears the rest. 0 for interest only."},"interest_amount":{"type":"string","description":"Interest paid, posted to Floor Plan Interest. 0 for principal only."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account the money left from, for example the bank account. Not a control account."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"paid_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date it was paid, the date of its journal entry. Defaults to today; never in the future, nor before the loan was financed."},"note":{"description":"Anything worth recording: the lender's statement or reference.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","principal_amount","interest_amount","ledger_account_id","ledger_account_code","ledger_account_name","paid_on","note","created_at"],"additionalProperties":false,"description":"A payment to a floor-plan lender: principal on one unit (a curtailment or the payoff), interest on it, or interest the lender billed for all its units. Rows are only ever added."},"FloorPlanLoanSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"status":{"type":"string","enum":["open","paid_off"],"description":"open: something is still owed on the unit. paid_off: the principal is all paid."},"floor_plan_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender. Its terms set the curtailments, the payoff date and the interest."},"floor_plan_lender_name":{"type":"string","description":"The lender's name, unique within the business."},"serialized_unit_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The unit financed. A unit has at most one loan that is not paid off."},"serial_number":{"type":"string","description":"The manufacturer's serial number, uppercase, unique within the item."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item the unit is one of."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"unit_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the unit is in stock now. Null when it is not in stock (sold, or sent back)."},"unit_location_name":{"description":"Display name, for example \"Boise Showroom\".","type":["string","null"]},"purchase_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The purchase order the lender paid the vendor for, when the loan is against an arrival."},"purchase_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"ledger_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The account the amount financed was debited to, when the lender did not pay a vendor for the unit's arrival: the bank for cash advanced against a unit already owned, Opening Balance Equity for a loan from before the business used the system. NULL when the lender paid the vendor."},"ledger_account_name":{"description":"The account name, unique within the tenant, for example \"Inventory\".","type":["string","null"]},"amount_financed":{"type":"string","description":"What the lender paid for the unit and the business owes for it."},"financed_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the lender paid, from which curtailments, interest and the age on the floor plan count. Defaults to today; never in the future."},"principal_paid":{"type":"string","description":"Principal paid back so far: curtailments and the payoff. Calculated from the payments."},"interest_paid":{"type":"string","description":"Interest recorded against this unit so far. Interest a lender bills for all its units at once is recorded against the lender, not here. Calculated from the payments."},"balance":{"type":"string","description":"What is still owed on the unit: amount_financed less principal_paid."},"days_floored":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days from financed_on to today, or to the day it was paid off."},"interest_accrued":{"type":"string","description":"Interest accrued from the end of the free days to today (or to the payoff), estimated at the lender's rate as it stands on each day's balance. Interest is booked only as the lender bills it."},"sold_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When an open loan's unit was handed over to a customer. Null while it is in stock."},"payoff_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a unit sold and not paid off: the date the payoff is due, by the lender's payoff_due_days."},"next_curtailment_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date of the first step of the lender's curtailment schedule not yet met. Null when none is left."},"next_curtailment_amount":{"description":"What is still to pay to meet that step: what should have been paid by then, less principal_paid.","type":["string","null"]},"paid_off_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the last of the principal was paid. NULL while anything is owed. Calculated."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","status","floor_plan_lender_id","floor_plan_lender_name","serialized_unit_id","serial_number","item_id","sku","item_name","unit_location_id","unit_location_name","purchase_order_id","purchase_order_number","ledger_account_id","ledger_account_name","amount_financed","financed_on","principal_paid","interest_paid","balance","days_floored","interest_accrued","sold_on","payoff_due_on","next_curtailment_on","next_curtailment_amount","paid_off_on","created_at","updated_at"],"additionalProperties":false,"description":"One serialized unit financed by a floor-plan lender: what was financed, what has been paid on it, and when it was paid off. What is owed and when is in floor_plan_loan_status."},"FloorPlanLoan":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"status":{"type":"string","enum":["open","paid_off"],"description":"open: something is still owed on the unit. paid_off: the principal is all paid."},"floor_plan_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender. Its terms set the curtailments, the payoff date and the interest."},"floor_plan_lender_name":{"type":"string","description":"The lender's name, unique within the business."},"serialized_unit_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The unit financed. A unit has at most one loan that is not paid off."},"serial_number":{"type":"string","description":"The manufacturer's serial number, uppercase, unique within the item."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item the unit is one of."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"unit_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the unit is in stock now. Null when it is not in stock (sold, or sent back)."},"unit_location_name":{"description":"Display name, for example \"Boise Showroom\".","type":["string","null"]},"purchase_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The purchase order the lender paid the vendor for, when the loan is against an arrival."},"purchase_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"ledger_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The account the amount financed was debited to, when the lender did not pay a vendor for the unit's arrival: the bank for cash advanced against a unit already owned, Opening Balance Equity for a loan from before the business used the system. NULL when the lender paid the vendor."},"ledger_account_name":{"description":"The account name, unique within the tenant, for example \"Inventory\".","type":["string","null"]},"amount_financed":{"type":"string","description":"What the lender paid for the unit and the business owes for it."},"financed_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the lender paid, from which curtailments, interest and the age on the floor plan count. Defaults to today; never in the future."},"principal_paid":{"type":"string","description":"Principal paid back so far: curtailments and the payoff. Calculated from the payments."},"interest_paid":{"type":"string","description":"Interest recorded against this unit so far. Interest a lender bills for all its units at once is recorded against the lender, not here. Calculated from the payments."},"balance":{"type":"string","description":"What is still owed on the unit: amount_financed less principal_paid."},"days_floored":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days from financed_on to today, or to the day it was paid off."},"interest_accrued":{"type":"string","description":"Interest accrued from the end of the free days to today (or to the payoff), estimated at the lender's rate as it stands on each day's balance. Interest is booked only as the lender bills it."},"sold_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When an open loan's unit was handed over to a customer. Null while it is in stock."},"payoff_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a unit sold and not paid off: the date the payoff is due, by the lender's payoff_due_days."},"next_curtailment_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date of the first step of the lender's curtailment schedule not yet met. Null when none is left."},"next_curtailment_amount":{"description":"What is still to pay to meet that step: what should have been paid by then, less principal_paid.","type":["string","null"]},"paid_off_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the last of the principal was paid. NULL while anything is owed. Calculated."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"payments":{"type":"array","items":{"$ref":"#/components/schemas/FloorPlanPayment"},"description":"Every payment on the unit, oldest first."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","status","floor_plan_lender_id","floor_plan_lender_name","serialized_unit_id","serial_number","item_id","sku","item_name","unit_location_id","unit_location_name","purchase_order_id","purchase_order_number","ledger_account_id","ledger_account_name","amount_financed","financed_on","principal_paid","interest_paid","balance","days_floored","interest_accrued","sold_on","payoff_due_on","next_curtailment_on","next_curtailment_amount","paid_off_on","created_at","updated_at","payments","next"],"additionalProperties":false,"description":"One serialized unit financed by a floor-plan lender: what was financed, what has been paid on it, and when it was paid off. What is owed and when is in floor_plan_loan_status."},"FloorPlanLoanPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FloorPlanLoanSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of floor-plan loans and the cursor for the next."},"FloorPlanInterest":{"type":"object","properties":{"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day counted."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day counted."},"periods":{"type":"array","items":{"type":"object","properties":{"floor_plan_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender."},"floor_plan_lender_name":{"type":"string","description":"The lender's name, unique within the business."},"month":{"type":"string","description":"The month, as YYYY-MM."},"interest_accrued":{"type":"string","description":"Interest accrued in the month (within from and to), estimated at the lender's rate on each day's balance."},"interest_paid":{"type":"string","description":"Interest recorded as paid to the lender with paid_on in the month."}},"required":["floor_plan_lender_id","floor_plan_lender_name","month","interest_accrued","interest_paid"],"additionalProperties":false},"description":"One row per lender and month, oldest first."}},"required":["from","to","periods"],"additionalProperties":false,"description":"Floor-plan interest per lender and month: accrued, estimated, and paid, as recorded."},"FloorPlanAudit":{"type":"object","properties":{"floor_plan_lender_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lender."},"floor_plan_lender_name":{"type":"string","description":"The lender's name, unique within the business."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location asked for, or null for every location."},"location_name":{"description":"That location's name.","type":["string","null"]},"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the audit is as of."},"units_open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units still owe this lender, here."},"balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What those units still owe. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What those units still owe. Null without cost.view."},"statuses":{"type":"object","properties":{"on_hand":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units are on_hand."},"held":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units are held."},"missing":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units are missing."},"sold":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units are sold."},"written_off":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units are written_off."},"not_in_stock":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units are not_in_stock."}},"required":["on_hand","held","missing","sold","written_off","not_in_stock"],"additionalProperties":false,"description":"How many of the units are in each status."},"units":{"type":"array","items":{"type":"object","properties":{"floor_plan_loan_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The loan."},"serialized_unit_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The unit financed. A unit has at most one loan that is not paid off."},"serial_number":{"type":"string","description":"The manufacturer's serial number, uppercase, unique within the item."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item the unit is one of."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"financed_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date the lender paid, from which curtailments, interest and the age on the floor plan count. Defaults to today; never in the future."},"days_floored":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days since the unit was financed."},"amount_financed":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the lender paid for the unit and the business owes for it. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the lender paid for the unit and the business owes for it. Null without cost.view."},"balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is still owed on the unit. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What is still owed on the unit. Null without cost.view."},"status":{"type":"string","enum":["on_hand","held","missing","sold","written_off","not_in_stock"],"description":"on_hand: in stock where location says, free to sell. held: in stock, held for a customer on an open order or a quote (held_for). missing: in stock by the books, but not found on the last count that covered its location (missing_on), and not moved since; find it before the auditor does. sold: handed over to a customer and not yet paid off (payoff_due_on). written_off: taken out of stock by an adjustment or a count, and still owed. not_in_stock: out of stock any other way (sent back to the vendor, or never received), and still owed."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the system says the unit is: in stock there, or for a unit no longer in stock, where it last left from."},"location_name":{"description":"That location's name.","type":["string","null"]},"held_for":{"anyOf":[{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order or quote holding it."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number people quote for it."},"customer_name":{"description":"Whom it is held for, or null on an order with no customer.","type":["string","null"]}},"required":["sales_order_id","order_number","customer_name"],"additionalProperties":false},{"type":"null"}],"description":"The order or quote holding the unit for a customer, or null when none does."},"sold_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a sold unit: the date it was handed over."},"payoff_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a sold unit: the date the lender expects it paid off."},"last_seen":{"anyOf":[{"type":"object","properties":{"stock_count_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The stock count."},"counted_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the count was posted. Set automatically."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location counted. Fixed once the count has lines."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."}},"required":["stock_count_id","counted_on","location_id","location_name"],"additionalProperties":false},{"type":"null"}],"description":"The latest posted stock count it was scanned on, or null when no count has seen it."},"missing_on":{"anyOf":[{"type":"object","properties":{"stock_count_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The stock count."},"counted_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the count was posted. Set automatically."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location counted. Fixed once the count has lines."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."}},"required":["stock_count_id","counted_on","location_id","location_name"],"additionalProperties":false},{"type":"null"}],"description":"For a missing unit: the count it was not found on. Null otherwise."}},"required":["floor_plan_loan_id","serialized_unit_id","serial_number","item_id","sku","item_name","financed_on","days_floored","amount_financed","balance","status","location_id","location_name","held_for","sold_on","payoff_due_on","last_seen","missing_on"],"additionalProperties":false},"description":"Every unit that still owes the lender, by location, item and serial number."}},"required":["floor_plan_lender_id","floor_plan_lender_name","location_id","location_name","as_of","units_open","balance","statuses","units"],"additionalProperties":false,"description":"A lender's floor-plan audit: every unit it financed that is not paid off, where the system says it is, what has happened to it, and when a stock count last saw it."},"Event":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"event_type":{"type":"string","enum":["customer.created","order.opened","order.completed","order.cancelled","sale.fulfilled","sale.returned","payment.received","payment.refunded","payment.reversed","appointment.scheduled","appointment.rescheduled","appointment.completed","appointment.cancelled","work_order.opened","work_order.completed","stock.received","water_test.recorded","appointment.en_route","appointment.arrived","appointment.failed","appointment.upcoming","delivery.site_not_ready","site_check.recorded","cash_drawer.opened","cash_drawer.closed","cash_drawer.reconciled","account_payment.received","account_payment.refunded","account_payment.reversed","gift_card.issued","gift_card.reloaded","gift_card.voided","gift_card.replaced","gift_card.cashed_out","gift_card.written_off","gift_card.reversed","receipt.issued","statement.issued","work_order.signed_off","message.failed","customer_reply.received","lead.created","lead.won","lead.lost"],"description":"What happened, as thing.happening: order.opened, order.completed, order.cancelled, sale.fulfilled, sale.returned, payment.received, payment.refunded (money given back), payment.reversed (a payment that never arrived, such as a bounced check, undone), appointment.scheduled, appointment.rescheduled, appointment.completed, appointment.cancelled, appointment.en_route (the crew set off), appointment.arrived, appointment.failed (a delivery refused or not possible), appointment.upcoming (a visit is tenant_policy.appointment_reminder_hours away), delivery.site_not_ready (a delivery's site is not shown ready by its deadline), site_check.recorded, work_order.opened, work_order.completed, stock.received, customer.created (a customer recorded, or a prospect who has just bought for the first time: never someone who has only asked), water_test.recorded, cash_drawer.opened, cash_drawer.closed (counted; reconciled too when within the tolerance), cash_drawer.reconciled, account_payment.received (money paid on account), account_payment.refunded (account credit given back), account_payment.reversed (a payment on account that never arrived, undone), gift_card.issued (a gift card sold or given away), gift_card.reloaded, gift_card.voided (a sale or reload taken back the same day), gift_card.replaced (what a card held moved to a new card; about the new card's first entry, which names the move out of the old one), gift_card.cashed_out, gift_card.written_off (breakage or unclaimed property), gift_card.reversed (a sale or reload whose money never arrived taken back; what was spent is owed by the buyer), receipt.issued (its payload leaves out the printed content), statement.issued (likewise), work_order.signed_off (a customer signed for work on a job: authorizing it, or accepting it as done), message.failed (a message the email or text service refused, or reported it could not deliver; its payload leaves out the subject and body), customer_reply.received (a customer texted the business; its payload leaves out what they wrote), lead.created (someone asked about buying: a lead captured), lead.won (an order linked to the lead was agreed) and lead.lost. No automation answers message.failed or customer_reply.received. Each gift card event's payload is the card's entry, never its code. Applying a payment on account to an order is a payment by the On account method, so it is payment.received as well, and so is spending a gift card. appointment.upcoming and delivery.site_not_ready are written when due by app.emit_due_events(), once per window of the visit."},"table_schema":{"type":"string","description":"Schema of the row the event is about: public for the core, or a module's schema. A module's events are visible only while the tenant has the module."},"table_name":{"type":"string","description":"Table of the row the event is about."},"row_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id of the row the event is about."},"payload":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"The row as it was when the event happened, as JSON. Decimal columns (money, quantities) are strings, such as \"100.00\"."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","event_type","table_schema","table_name","row_id","payload","created_at"],"additionalProperties":false,"description":"Something that happened, written by the database in the same transaction as the fact itself: an order opened, goods handed over, a payment received. The feed that integrations, automations and agents react to. Read it through app.next_events(), which gives a safe order."},"EventPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of events."},"NewEvents":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/Event"},"description":"The next events after the consumer's cursor, in a safe order. Handle them, then acknowledge the last one."},"more":{"type":"boolean","description":"True when there may be more events waiting: acknowledge these, then ask again."}},"required":["events","more"],"additionalProperties":false,"description":"Events a consumer has not yet finished with."},"EventConsumer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What reads the feed, unique within the tenant: \"quickbooks\", \"automations\"."},"last_event_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The last event the consumer finished with: the last one getNewEvents returned. NULL before it has read anything."},"is_active":{"type":"boolean","description":"False once the consumer is retired."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","last_event_id","is_active","created_at","updated_at"],"additionalProperties":false,"description":"One reader of the event feed (an adapter, the automation runner, an agent) and the last event it has finished with. A consumer that stops and starts again carries on from there."},"EventConsumerPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EventConsumer"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of event consumers."},"ExternalLink":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"system":{"type":"string","description":"The outside system, as a short lowercase key: quickbooks, stripe, woocommerce."},"table_name":{"type":"string","description":"The table of the row here."},"row_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The id of the row here."},"external_id":{"type":"string","description":"The record's id in the outside system."},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Whatever else the adapter needs to keep about the outside record, such as a version token for updates."},"is_active":{"type":"boolean","description":"False once the outside record is gone or the link was wrong."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","system","table_name","row_id","external_id","metadata","is_active","created_at","updated_at"],"additionalProperties":false,"description":"A statement that one row here is one record in an outside system: this customer is that QuickBooks customer, this payment is that Stripe charge, this item is that web store product. An adapter looks here before creating anything outside, so nothing is created twice."},"ExternalLinkPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExternalLink"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of external links."},"JournalExport":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"system":{"type":"string","description":"The accounting system the batch is for: quickbooks."},"through_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The batch holds every entry dated on or before this that was not already in a batch."},"status":{"type":"string","enum":["pending","exported","failed"],"description":"pending: made, not yet confirmed received. exported: the accounting system has it. failed: it was refused; status_reason says why, and the adapter tries the same batch again."},"status_reason":{"description":"Why the batch failed.","type":["string","null"]},"entry_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many entries are in the batch. Calculated."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"journal_entry_ids":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"description":"The entries in the batch, in date order. Read them with getJournalEntry."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","system","through_date","status","status_reason","entry_count","next","journal_entry_ids","created_at","updated_at"],"additionalProperties":false,"description":"One batch of journal entries sent to the tenant's accounting system. Made by app.create_journal_export(), which puts each entry in exactly one batch, so nothing is exported twice and nothing is missed."},"JournalExportPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/JournalExport"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of export batches."},"StockAging":{"type":"object","properties":{"bucket_days":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"The ages, in days on hand, where the stock aging read starts a new bucket, in increasing order: the default 90, 180, 270 gives 0-90, 91-180, 181-270 and over 270."},"locations":{"type":"array","items":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"value":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The value of all stock on hand there. Null without cost.view."},"buckets":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The bucket's ages in days, for example 91-180, or 271+ for the last."},"from_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The youngest age in the bucket, in days on hand."},"to_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The oldest age in the bucket. Null for the last, which has no limit."},"serialized_units":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many serialized units in stock fall in the bucket."},"lines":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many arrivals of quantity stock still on hand fall in the bucket (a FIFO layer, or an arrival at average cost)."},"value":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the stock in the bucket cost. Null without cost.view."},"floored_units":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of its serialized units are on a floor plan not paid off."},"floor_plan_balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What is still owed to lenders on those units. Null without cost.view."},"interest_accrued":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Interest accrued so far on those units' floor plans, estimated at each lender's rate: what they have cost to hold. Null without cost.view."}},"required":["label","from_days","to_days","serialized_units","lines","value","floored_units","floor_plan_balance","interest_accrued"],"additionalProperties":false},"description":"Every bucket, youngest first, empty ones included."}},"required":["location_id","location_name","value","buckets"],"additionalProperties":false},"description":"Each location holding stock, by name."}},"required":["bucket_days","locations"],"additionalProperties":false,"description":"Stock on hand by how long it has been on hand, per location, in the business's buckets."},"StockLevel":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"Units on hand, in the item's unit. Negative when more has been issued than received and the tenant allows that. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_committed":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are sold on open orders and not yet handed over, to be handed over from here: from the order's location, or for a serialized unit chosen on the line, from where that unit is. A quote counts only for a serialized unit it names and holds until it expires. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_available":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"quantity_on_hand less quantity_committed: how many can still be sold from here without taking them from a customer. Negative when more is sold than is on hand. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_on_order":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are still expected from vendors for delivery here: on purchase orders sent and not closed, what each line ordered less what has arrived. Drafts are not counted. A decimal string with up to 3 decimal places, for example \"12.500\"."},"value_on_hand":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Cost of the stock on hand. Divided by quantity_on_hand it is the average cost. Negative while quantity_on_hand is negative. Null without cost.view."},"average_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"Value on hand divided by quantity on hand. Null when nothing is on hand. Null without cost.view."},"last_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"Unit cost of the most recent movement into stock. NULL until something is received. Null without cost.view."}},"required":["item_id","sku","item_name","location_id","location_name","quantity_on_hand","quantity_committed","quantity_available","quantity_on_order","value_on_hand","average_cost","last_cost"],"additionalProperties":false,"description":"How much of one item is on hand at one location, and what that stock is worth. Derived: kept in step with stock_movement by the database and never written directly. A missing row means the item has never moved at that location."},"StockLevelPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockLevel"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of stock levels and the cursor for the next."},"StockMovement":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location whose stock changed."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"kind":{"type":"string","enum":["opening","adjustment","sale","sale_return","receipt","vendor_return","transfer_out","transfer_in","count","revaluation","landed_cost"],"description":"Why stock changed. opening: stock on hand when the tenant started using the system. adjustment: a correction for damage, loss or a find, with its reason (adjustment_reason_id). sale: goods handed to a customer. sale_return: goods a customer brought back. receipt: goods received from a vendor. vendor_return: goods sent back to a vendor. transfer_out and transfer_in: the two halves of a move between locations. count: the difference found by a stock count. revaluation: a serialized unit in stock written down to a lower cost, with quantity 0 and its reason. landed_cost: freight, a crane or prep paid after the goods arrived, added to what stock on hand cost, with quantity 0. trade_in: a customer's old unit taken in toward a sale, at what they were allowed for it, or given back to them (trade_in, 0008). Only opening, adjustment and revaluation are written directly; the others are written by the record named in source_table."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much stock changed, in the item's unit: positive into stock, negative out of it. Exactly 1 or -1 for a serialized unit. 0 for a revaluation or a landed cost, and only for those. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"Cost per unit. For stock coming in, send either this or extended_cost; with neither, the current average cost is used. For stock going out it is calculated and any value sent is ignored. Rounded to four places: extended_cost is the exact figure. For a revaluation: the unit's new cost, which is sent. For a landed cost: what it added per unit of the stock it went to. Null without cost.view."},"extended_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Total cost of the movement, always positive. For stock coming in, send it instead of unit_cost when the total is what is known exactly: a vendor invoice line, a transfer, a return. For stock going out it is calculated by the tenant's costing method and is the cost of the goods. For a serialized unit it is the unit's own cost. For a revaluation it is the amount written off: the unit's old cost less its new one. For a landed cost it is the amount added to stock value. Calculated. Null without cost.view."},"value_change":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"How much the value of stock on hand changed: positive in, negative out. Calculated. For stock coming in, value_change plus cost_variance equals extended_cost. Null without cost.view."},"cost_variance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Only on stock coming in that fills negative stock: its actual cost minus the cost assumed when that stock was issued early. Positive means those goods cost more than assumed. Calculated. Null without cost.view."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The individual unit that moved. Required for a serial-tracked item, not allowed otherwise."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"stock_lot_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an item tracked by lot, the lot. Coming in: the lot that arrived; left out, the item's unknown lot, where the tenant allows that. Going out: the lot to take from; left out, the system takes from the first lot to expire, then the oldest. Set to the lot that moved when it was one, and NULL when it was several: stock_movement_lot lists exactly which lots moved and how many of each. Not allowed for an item not tracked by lot."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known. Of stock_lot_id.","type":["string","null"]},"lots":{"type":"array","items":{"$ref":"#/components/schemas/MovedLot"},"description":"For an item tracked by lot: each lot the movement took or brought, and how many. Empty otherwise."},"source_table":{"description":"The table of the record that caused the movement, for example sales_fulfilment. NULL for a movement written directly.","type":["string","null"]},"source_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The id of the record that caused the movement. NULL for a movement written directly."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a sale or a return: the order the goods were handed over or returned on."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a sale or a return: the customer, or null for an anonymous counter sale."},"customer_name":{"description":"The customer's name.","type":["string","null"]},"purchase_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a receipt or a return to the vendor: the purchase order it was on."},"purchase_order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a receipt or a return to the vendor: the vendor."},"vendor_name":{"description":"The vendor's name.","type":["string","null"]},"adjustment_reason_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Why stock was lost, damaged or found: the business's reason. Required for an adjustment or write-down the application writes. A count's difference carries its line's reason, else the business's count reason. NULL for every other kind, and for stock an engine wrote off on its own (a trade-in disposed of)."},"adjustment_reason_code":{"description":"A short code for the reason, unique within the business, uppercase letters, digits, _ and -: BROKEN, THEFT. Fixed once the reason exists. Null when the movement has no reason.","type":["string","null"]},"adjustment_reason_name":{"description":"The reason as staff choose it, for example \"Broken or spilled\". Null when the movement has no reason.","type":["string","null"]},"note":{"description":"What happened, in words. Required for an adjustment or write-down whose reason asks for one (note_required). For a movement another record wrote, that record's note.","type":["string","null"]},"sent_back_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"For a loss (an adjustment out): how much of what it wrote off has since gone back to the vendor as written off (returnToVendor with written_off_stock_movement_id), which puts it back on the books first. What is left can still go back that way. Null for any other movement. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"For a loss (an adjustment out): how much of what it wrote off has since gone back to the vendor as written off (returnToVendor with written_off_stock_movement_id), which puts it back on the books first. What is left can still go back that way. Null for any other movement."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","item_id","sku","item_name","location_id","location_name","kind","quantity","unit_cost","extended_cost","value_change","cost_variance","serialized_unit_id","serial_number","stock_lot_id","lot_code","lots","source_table","source_id","sales_order_id","order_number","customer_id","customer_name","purchase_order_id","purchase_order_number","vendor_id","vendor_name","adjustment_reason_id","adjustment_reason_code","adjustment_reason_name","note","sent_back_quantity","created_at"],"additionalProperties":false,"description":"One change in the stock of one item at one location. This is the stock ledger: rows are only ever added, and a mistake is corrected by a new movement in the opposite direction. On-hand quantity and value are the sum of these rows."},"StockMovementPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockMovement"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of the stock ledger and the cursor for the next."},"StockAdjustment":{"type":"object","properties":{"movements":{"type":"array","items":{"$ref":"#/components/schemas/StockMovement"},"description":"The movements written, one per line, in the order sent."}},"required":["movements"],"additionalProperties":false,"description":"Stock put on hand or corrected by hand, as stock movements."},"UnitPromise":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The quote or order holding it."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number people quote for that order."},"sales_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order line holding it."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"That line's number on its order."},"status":{"type":"string","enum":["quote","open"],"description":"quote: offered, holding the unit until expires_on. open: agreed, holding it until it is handed over."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Whom it is promised to. NULL on an order with no customer."},"customer_name":{"description":"That customer's name.","type":["string","null"]},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For a quote: the last date it holds the unit. NULL for an open order, or a quote with no expiry date."}},"required":["sales_order_id","order_number","sales_order_line_id","line_number","status","customer_id","customer_name","expires_on"],"additionalProperties":false,"description":"One row for each serialized unit an order line has promised to a customer and not yet handed over: on an open order, or on a quote that has not expired when the business's quotes hold units. Not stored. A unit with no row here and a location is free to sell."},"SerializedUnit":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"serial_number":{"type":"string","description":"The manufacturer's serial number, uppercase, unique within the item."},"cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What this unit cost the tenant. The unit enters and leaves stock at exactly this cost. It can be corrected until the unit first moves into stock. From then on only a write-down changes it, lowering it while the unit is in stock. Null without cost.view."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the unit is in stock. NULL when it is not in stock: not yet received, or sold. Set only by stock movements."},"location_name":{"description":"Where it is in stock, or null when it is not in stock.","type":["string","null"]},"promised_to":{"anyOf":[{"$ref":"#/components/schemas/UnitPromise"},{"type":"null"}],"description":"The open order, or the quote that has not expired, that has promised this unit to a customer. Null when no one holds it: a unit in stock with promised_to null is free to sell."},"item_configuration_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a model built with options: how this unit is built. Set from the purchase order line it arrived on; recorded by hand for a unit that came another way. NULL when not recorded. An order line naming the unit takes it."},"options":{"type":"array","items":{"$ref":"#/components/schemas/ChosenOption"},"description":"The options the unit is built with, group by group. Empty when none are recorded."},"notes":{"description":"Free-form notes for staff, for example freight damage or a floor-model history.","type":["string","null"]},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","item_id","sku","item_name","serial_number","cost","location_id","location_name","promised_to","item_configuration_id","options","notes","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"One individual unit of a serial-tracked item, for example one hot tub with its serial number. The unit keeps its identity for life: in stock, sold, installed at a site, serviced under warranty. Its place in stock changes only through stock movements. schema.org: IndividualProduct."},"SerializedUnitPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SerializedUnit"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of serialized units and the cursor for the next."},"StockTransfer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"from_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock leaves."},"from_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"to_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock arrives at."},"to_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are moved. Exactly 1 for a serialized unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The individual unit moved. Required for a serial-tracked item, not allowed otherwise."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"stock_lot_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an item tracked by lot: the lot to move. Left out, the system moves the first lots to expire, then the oldest. The stock arrives in the same lots it left in; the movements say which."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known. Of stock_lot_id.","type":["string","null"]},"lots":{"type":"array","items":{"$ref":"#/components/schemas/MovedLot"},"description":"For an item tracked by lot: each lot moved, and how many. They arrived in the same lots."},"extended_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cost of the stock moved: what it left with and arrived at. Calculated. Null without cost.view."},"stock_transfer_batch_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The transfer of many lines this is one line of, when it moved with others. NULL for an item moved on its own."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who moved it."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"transferred_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the move, at the location it left. Calculated."},"note":{"description":"Why it was moved.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","item_id","sku","item_name","from_location_id","from_location_name","to_location_id","to_location_name","quantity","serialized_unit_id","serial_number","stock_lot_id","lot_code","lots","extended_cost","stock_transfer_batch_id","staff_member_id","staff_member_name","transferred_on","note","created_at"],"additionalProperties":false,"description":"Stock of one item moved from one location to another. Rows are only ever added; to undo a transfer, transfer it back. The stock leaves one location and arrives at the other in the same step, at exactly the cost it left with."},"StockTransferPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockTransfer"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of transfers and the cursor for the next."},"StockCount":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"status":{"type":"string","enum":["open","posted","cancelled"],"description":"open: being counted. posted: stock has been corrected to the count. cancelled: abandoned, nothing changed. Posted and cancelled are final."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location counted. Fixed once the count has lines."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is responsible for the count."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"counted_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the count was posted. Set automatically."},"notes":{"description":"Free-form notes: what was counted and why.","type":["string","null"]},"lines":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"stock_lot_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an item tracked by lot: the lot counted, each lot on a line of its own. NULL counts the unknown lot: stock whose lot cannot be read. Lots not on the count are not touched. NULL for an item not tracked by lot."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known. Null for the unknown lot, or an item not tracked by lot.","type":["string","null"]},"counted_quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many were found on the shelf. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_before":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"What the system held at the moment the item was counted (updated_at). The difference from counted_quantity is what posting corrects, so sales and receipts between counting and posting are not undone. Another count of the item at the location posted in between makes the difference stale: posting is then refused until the item is counted again. Calculated. As a decimal string."},"difference":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"counted_quantity less quantity_before: what posting will add (positive) or remove."},"adjustment_reason_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Why the item was found over or short, when whoever posts the count knows: theft, breakage. NULL puts the difference down to the business's count reason (count error, by default). Set only while the count is open."},"adjustment_reason_name":{"description":"The reason's name. Null when the line names none.","type":["string","null"]},"recount_needed":{"type":"boolean","description":"True when another stock count has corrected this item here since it was counted on this one, so its difference is stale and posting is refused. Record it again with recordStockCount. While the count is blind to this key, true whenever another count has corrected the item since, whatever was counted."}},"required":["item_id","sku","item_name","stock_lot_id","lot_code","counted_quantity","quantity_before","difference","adjustment_reason_id","adjustment_reason_name","recount_needed"],"additionalProperties":false},"description":"The items counted so far, by SKU, and for an item tracked by lot, lot by lot."},"covers_units":{"type":"boolean","description":"True when the count covers the serialized units at its location (spas, swim spas): each is scanned by its serial number, and posting reconciles every unit recorded here, so one not scanned is missing. One such count is open at a location at a time. False: serialized units are not counted and not touched. Fixed once the count is started."},"blind":{"type":"boolean","description":"True while what the system holds is kept from this key: the count is open, the business counts blind (stock_count_blind), and the key does not hold purchase.manage at the location. quantity_before and difference are then null, and units lists only what was scanned, without where each was recorded or what posting will do."},"units":{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The scan's id, to take it back with removeStockCountUnit. Null for a unit not scanned, shown before posting."},"serial_number":{"type":"string","description":"The serial number as scanned or typed, uppercase. For a unit not scanned, its own."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The unit with this serial number. NULL when the system has none: the serial was mistyped, or the unit was never recorded. Calculated."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item the unit is one of. Sent only when a serial number is on units of more than one item; otherwise set from the unit. NULL for a serial number no unit has, unless sent."},"sku":{"description":"The tenant's own stock code for the item, uppercase, unique within the tenant. Null when no item is known.","type":["string","null"]},"item_name":{"description":"Short name shown on screens, receipts and invoices. Null when no item is known.","type":["string","null"]},"scanned":{"type":"boolean","description":"True for a serial scanned on the count. False for a unit expected and not scanned. Calculated."},"known":{"type":"boolean","description":"True when a unit with this serial number exists. False for a serial the system has never seen: often a typo, so check it against the data plate."},"recorded_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the system had the unit in stock when it was scanned, or when it was listed as expected. NULL when it was not in stock. Calculated. Null while the count is blind to this key: open, the business counts blind (stock_count_blind), and the key does not hold purchase.manage at the count's location."},"recorded_location_name":{"description":"That location's name, or null when it is null. Null while the count is blind to this key: open, the business counts blind (stock_count_blind), and the key does not hold purchase.manage at the count's location.","type":["string","null"]},"result":{"anyOf":[{"type":"string","enum":["found","moved_here","back_in_stock","not_in_stock","unknown","missing","written_off","rescan","arrived_since"]},{"type":"null"}],"description":"What posting found and did. found: in stock here, as recorded. moved_here: recorded at another location; a transfer brought it here. back_in_stock: an adjustment or count had taken it out of stock; it came back in here. not_in_stock: sold, sent back to the vendor or never received, so nothing changed; look into it. unknown: no unit has this serial number, so nothing changed; look into it. missing: expected here and not scanned; left in stock and flagged. written_off: expected here and not scanned; taken out of stock, as the business's policy says. NULL while the count is open, for a removed scan, and for a unit expected that left in the ordinary way before posting. Calculated. While the count is open, what posting it now would do: also rescan (the unit has moved since it was scanned; posting is refused until it is scanned again or the scan removed) and arrived_since (it came here after the scanning began and was not scanned; it is not judged). Null while the count is blind to this key: open, the business counts blind (stock_count_blind), and the key does not hold purchase.manage at the count's location."},"rescan_needed":{"type":"boolean","description":"True when the unit has moved since it was scanned (sold, moved or counted elsewhere), so posting is refused until it is scanned again with recordStockCountUnits, or the scan taken back with removeStockCountUnit. Shown even while the count is blind."},"scanned_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was last scanned. Null for a unit not scanned."}},"required":["id","serial_number","serialized_unit_id","item_id","sku","item_name","scanned","known","recorded_location_id","recorded_location_name","result","rescan_needed","scanned_at"],"additionalProperties":false},"description":"For a count that covers units, by serial number: each serial scanned, and (once posted, or before it to a key the count is not blind to) each unit expected here that was not scanned, or that arrived after the scanning began. Removed scans are left out. Empty for a count that does not cover units. A unit moved here shows in listStockTransfers."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","status","location_id","location_name","staff_member_id","staff_member_name","counted_on","notes","lines","covers_units","blind","units","next","created_at","updated_at"],"additionalProperties":false,"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted."},"StockCountPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockCount"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of stock counts and the cursor for the next."},"StockLot":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known.","type":["string","null"]},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"Units of the lot on hand across every location (only the location asked for, when filtered by one). A decimal string with up to 3 decimal places, for example \"12.500\"."},"locations":{"type":"array","items":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"Units of the lot on hand, in the item's unit. Negative only where the tenant allows negative stock and more of the lot has been taken than was recorded arriving. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["location_id","location_name","quantity_on_hand"],"additionalProperties":false},"description":"Where the lot is on hand, or has been: each location it has moved at."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","item_id","sku","item_name","lot_code","expires_on","quantity_on_hand","locations","created_at","updated_at"],"additionalProperties":false,"description":"One lot (batch) of an item tracked by lot: the units a vendor made together and printed one lot code on. Stock of the lot is in stock_lot_level, and which movements took or brought it in stock_movement_lot. schema.org: SomeProducts."},"StockLotPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockLot"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of lots and the cursor for the next."},"StockAdjustmentReason":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","description":"A short code for the reason, unique within the business, uppercase letters, digits, _ and -: BROKEN, THEFT. Fixed once the reason exists."},"name":{"type":"string","description":"The reason as staff choose it, for example \"Broken or spilled\"."},"ledger_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The expense account what is lost for this reason posts to, for example Store Supplies for goods used in the showroom; what is found is credited there. NULL (the default) posts to Inventory Adjustments. Only an expense account the system does not post its own events to. A change applies to stock moved from then on."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200. Of the account its cost posts to: Inventory Adjustments when the reason names none."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"note_required":{"type":"boolean","description":"True when each adjustment for this reason must also say what happened in its note, for example \"Other\". False (the default) lets the reason speak for itself."},"is_count_reason":{"type":"boolean","description":"True for the one reason a stock count's differences are put down to when no other is given: count error, by default. At most one per business, and only an active one. With none, a count's differences carry no reason."},"is_active":{"type":"boolean","description":"False once the reason is no longer used: no new adjustment can name it. Stock already moved for it keeps it."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","code","name","ledger_account_id","ledger_account_code","ledger_account_name","note_required","is_count_reason","is_active","created_at","updated_at"],"additionalProperties":false,"description":"Why stock was lost, damaged or found, as the business names it: broken, expired, theft, used in the showroom, damaged in freight, found. An adjustment, a write-down and a stock count's differences each carry one, so losses can be read by cause and store. A new business starts with a few, which it may rename, retire or add to."},"StockAdjustmentReasonPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockAdjustmentReason"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of reasons for stock lost or found."},"ParLevel":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item, a good kept in stock. Fixed once the level exists."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location, premises or a vehicle. Fixed once the level exists."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"location_kind":{"type":"string","enum":["premises","vehicle"],"description":"premises: a showroom, warehouse or service base. vehicle: a service van or truck that carries stock; its stock is counted and moved like any location's, but nothing is sold, scheduled or staffed from it. Fixed once the location exists."},"minimum_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The reorder point: when what the location holds, with what is on order and less what is promised to customers, is at or below this, bring it back up to par. NULL: bring it back as soon as it is below par. Less than par_quantity."},"par_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The quantity the location is brought back up to: order up to. NULL once the location no longer keeps a level for the item."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units on hand there now. A decimal string with up to 3 decimal places, for example \"12.500\"."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","item_id","sku","item_name","location_id","location_name","location_kind","minimum_quantity","par_quantity","quantity_on_hand","created_at","updated_at"],"additionalProperties":false,"description":"How much of one item one location keeps on hand: the par it is brought back up to, and the minimum it may fall to first. getReplenishment suggests what brings it back to par, by purchase order at premises and by transfer from its home for a vehicle; getStockRestock lists a vehicle's restock from it."},"ParLevelPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ParLevel"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of par levels."},"SetParLevelsResult":{"type":"object","properties":{"levels":{"type":"array","items":{"$ref":"#/components/schemas/ParLevel"},"description":"Each level sent, as it now stands. A cleared one has no par."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["levels","next"],"additionalProperties":false,"description":"The par levels set."},"RestockLine":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"gtin":{"description":"The barcode number printed on the product (UPC, EAN or other GTIN), digits only.","type":["string","null"]},"whole_units":{"type":"boolean","description":"True when the item is sold, bought, moved and counted only in whole units of unit_text, as a filter cartridge or a cover is: a quantity with a fraction is refused everywhere. False when part of a unit makes sense, as 2.5 lb of a chemical or 1.5 hours of labor. Always true for a serial-tracked item."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units on hand at the location being restocked. A decimal string with up to 3 decimal places, for example \"12.500\"."},"minimum_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The reorder point: when what the location holds, with what is on order and less what is promised to customers, is at or below this, bring it back up to par. NULL: bring it back as soon as it is below par. Less than par_quantity."},"par_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The quantity the location is brought back up to: order up to. NULL once the location no longer keeps a level for the item. Null when it keeps none."},"quantity_to_replace":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units used here and not yet replaced: what left by being sold, used on a job, lost or counted short, less what has come in since (by any means), never below 0. A transfer out or a return to the vendor is not use. A van's restock brings these back for items it keeps no par level of. A decimal string with up to 3 decimal places, for example \"12.500\"."},"reason":{"type":"string","enum":["below_par","used"],"description":"Why it is on the list. below_par: the location keeps a par level of it and holds less. used: it keeps no par level of it, and some was used and not yet replaced."},"quantity_wanted":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units that would bring it back: up to par, or what was used, in whole units for an item sold whole. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_available":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units the location restocking it holds and has not promised to customers, at most what can be moved. A decimal string with up to 3 decimal places, for example \"12.500\"."},"suggested_quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units to move: quantity_wanted, or quantity_available when that is less. A decimal string with up to 3 decimal places, for example \"12.500\"."},"source_minimum_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The minimum the location restocking it keeps of it, if any: moving suggested_quantity can take it to or below that, and getReplenishment then reorders it there."}},"required":["item_id","sku","item_name","gtin","whole_units","quantity_on_hand","minimum_quantity","par_quantity","quantity_to_replace","reason","quantity_wanted","quantity_available","suggested_quantity","source_minimum_quantity"],"additionalProperties":false,"description":"One item to bring to a location, and why."},"StockRestock":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location being restocked: a van, usually."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"from_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock comes from: the van's home, unless another was named."},"from_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/RestockLine"},"description":"What to bring, by SKU. Only items with something wanted."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["location_id","location_name","from_location_id","from_location_name","lines","next"],"additionalProperties":false,"description":"What to bring to a location to restock it, from where."},"StockTransferBatch":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"from_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock leaves."},"from_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"to_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock arrives at."},"to_location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who moved it."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"transferred_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the move, at the location it left. Calculated."},"note":{"description":"Why it was moved.","type":["string","null"]},"extended_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cost of everything moved: its lines' extended_cost added up. Null without cost.view."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/StockTransfer"},"description":"Each item moved, in the order sent."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","from_location_id","from_location_name","to_location_id","to_location_name","staff_member_id","staff_member_name","transferred_on","note","extended_cost","lines","created_at"],"additionalProperties":false,"description":"Several items moved from one location to another together, as one transfer of many lines: a van restocked from its home store. Its lines are the stock_transfer rows naming it, all moved in the same step. Rows are only ever added."},"Location":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"branch_code":{"type":"string","pattern":"^[A-Z0-9]{2,8}$","description":"Short uppercase code staff use to name the location, unique within the tenant, for example BOI."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name, for example \"Boise Showroom\"."},"time_zone":{"type":"string","description":"IANA time zone, for example America/Boise. Business days and opening hours are evaluated in it."},"street_address":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"Street address. A second line (suite, unit) follows a line break."},"address_locality":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"City or town."},"address_region":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"State or province code, for example ID."},"postal_code":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}],"description":"ZIP or postal code."},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"telephone":{"anyOf":[{"type":"string","pattern":"^\\+[1-9]\\d{6,14}$"},{"type":"null"}],"description":"Public phone number in E.164 form, for example +12085550100."},"tax_area_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The tax area the location is in. Sales the customer carries out of this location are taxed by it. NULL for a vehicle: nothing is carried out of a van."},"is_active":{"type":"boolean","description":"False once the location has closed. Closed locations stay for history and are never deleted."},"home_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a vehicle: the premises it works from and is restocked from, which must be open. A role held at that location holds for the vehicle too. Its time zone is the vehicle's. NULL for premises."},"driver_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a vehicle: the technician who normally drives it, at most one active vehicle each. Parts that technician records as used on a job at a customer's site come from this vehicle unless another location is named. NULL for premises."},"price_list_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The store's regular price list: its entries set what items sell for at this store, and an item with no entry on it sells at its own price. Stores naming the same list share one regional price. NULL: every item sells at its own price. Only a regular list can be named here; an offer list (a trade, employee or contract price, or a sale) competes with the regular price wherever it applies. Changing it needs price_list.manage. Orders already written keep their prices: repriceSalesOrder brings a quote up to date. Accepts its id or its code."},"kind":{"type":"string","enum":["premises","vehicle"],"description":"premises: a showroom, warehouse or service base. vehicle: a service van or truck that carries stock; its stock is counted and moved like any location's, but nothing is sold, scheduled or staffed from it. Fixed once the location exists."},"tax_area_name":{"description":"The tax area's name, or null when the location has none.","type":["string","null"]},"home_location_branch_code":{"description":"For a vehicle: its home location's branch code. Null for premises.","type":["string","null"]},"driver_staff_member_name":{"description":"For a vehicle: its driver's name, or null when it has none or is premises.","type":["string","null"]},"price_list_code":{"description":"The code of the store's regular price list, or null when it sells at the items' own prices.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","branch_code","name","time_zone","street_address","address_locality","address_region","postal_code","address_country","telephone","tax_area_id","is_active","home_location_id","driver_staff_member_id","price_list_id","kind","tax_area_name","home_location_branch_code","driver_staff_member_name","price_list_code","created_at","updated_at"],"additionalProperties":false,"description":"A physical place the tenant operates: showroom, warehouse or service base. Stock, registers, sales and staff hang off a location. schema.org: LocalBusiness with a PostalAddress."},"LocationPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of locations and the cursor for the next."},"PriceListStore":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A store the list applies at."},"branch_code":{"type":"string","description":"Its branch code."},"location_name":{"type":"string","description":"Its name."}},"required":["location_id","branch_code","location_name"],"additionalProperties":false,"description":"A store an offer list is limited to. An offer list with no active rows here applies at every store; with some, only at those. A regular list is not limited here: the stores that name it in location.price_list_id are its stores."},"PriceListSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","description":"Short uppercase code staff and order lines name the list by, for example CONTR or WOODRIVER. Unique within the tenant."},"name":{"type":"string","description":"Display name, for example \"Contractor\" or \"Wood River stores\"."},"description":{"description":"What the list is for and who gets it, for staff.","type":["string","null"]},"kind":{"type":"string","enum":["regular","offer"],"description":"regular: a store's or region's own prices, named by stores in location.price_list_id; fixed prices by item or pack only. offer: competes with the regular price wherever it applies, the lowest winning; prices or percents off, by item, pack or category. Fixed once the list exists."},"audience":{"type":"string","enum":["everyone","assigned"],"description":"Who an offer list is for. everyone: every sale where it applies, as a store-wide sale. assigned: only customers put on it (customer_price_list), as a trade, employee or contract price. Always everyone on a regular list."},"is_active":{"type":"boolean","description":"False once the list is retired: it prices no new line, and lines already priced from it keep their prices."},"stores":{"type":"array","items":{"$ref":"#/components/schemas/PriceListStore"},"description":"Where the list applies. For a regular list: the stores that name it as theirs (location.price_list_id). For an offer list: the stores it is limited to; empty applies at every store."},"customers_assigned":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"For an assigned offer list: how many customers are on it today. 0 for any other list."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","code","name","description","kind","audience","is_active","stores","customers_assigned","created_at","updated_at"],"additionalProperties":false,"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog."},"PriceListEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item priced. NULL when the entry is for a category."},"sku":{"description":"The item's SKU. Null for a category's entry.","type":["string","null"]},"item_name":{"description":"The item's name. Null for a category's entry.","type":["string","null"]},"item_category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an offer list only: prices every item filed directly under this category (not its subcategories), as a percent off or a fixed price. NULL when the entry is for an item."},"category_name":{"description":"The category's name. Null for an item's entry.","type":["string","null"]},"pack_text":{"description":"For a price on one of the item's packs (case, bag): the pack, as the item's pack is called. The price is then for one pack, and min_quantity counts packs. NULL prices the item's own unit, and a pack of it at that unit price times the pack size.","type":["string","null"]},"min_quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The smallest quantity on one line the price applies to: a quantity break. In packs for a pack's entry, otherwise in the item's unit. 0 (the default) applies to any quantity. Lines are not added together: each line is priced on its own quantity. schema.org: eligibleQuantity. A decimal string with up to 3 decimal places, for example \"12.500\"."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The price of one unit (or one pack) before tax, options added on. NULL when the entry is a percent off. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The price of one unit (or one pack) before tax, options added on. NULL when the entry is a percent off."},"percent_off":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"For an offer list only: the percent taken off the regular price (with any options), rounded to the cent per unit. NULL when the entry is a fixed price. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For an offer list only: the percent taken off the regular price (with any options), rounded to the cent per unit. NULL when the entry is a fixed price."},"valid_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first order date the price applies to. The order's date, which also picks its tax rates, decides."},"valid_through":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last order date the price applies to. NULL: until a later entry replaces it or it is ended."},"reason":{"type":"string","description":"The entry as a person reads it, for example \"CONTR: 10% off Water Care, 3 or more\"."}},"required":["id","item_id","sku","item_name","item_category_id","category_name","pack_text","min_quantity","price","percent_off","valid_from","valid_through","reason"],"additionalProperties":false,"description":"One price on a price list: for an item, one of its packs, or every item filed directly under a category; from a quantity on one line, over a span of dates. A price change is a new entry with a later valid_from, so lines already priced keep the entry they were priced from. schema.org: UnitPriceSpecification."},"PriceList":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","description":"Short uppercase code staff and order lines name the list by, for example CONTR or WOODRIVER. Unique within the tenant."},"name":{"type":"string","description":"Display name, for example \"Contractor\" or \"Wood River stores\"."},"description":{"description":"What the list is for and who gets it, for staff.","type":["string","null"]},"kind":{"type":"string","enum":["regular","offer"],"description":"regular: a store's or region's own prices, named by stores in location.price_list_id; fixed prices by item or pack only. offer: competes with the regular price wherever it applies, the lowest winning; prices or percents off, by item, pack or category. Fixed once the list exists."},"audience":{"type":"string","enum":["everyone","assigned"],"description":"Who an offer list is for. everyone: every sale where it applies, as a store-wide sale. assigned: only customers put on it (customer_price_list), as a trade, employee or contract price. Always everyone on a regular list."},"is_active":{"type":"boolean","description":"False once the list is retired: it prices no new line, and lines already priced from it keep their prices."},"stores":{"type":"array","items":{"$ref":"#/components/schemas/PriceListStore"},"description":"Where the list applies. For a regular list: the stores that name it as theirs (location.price_list_id). For an offer list: the stores it is limited to; empty applies at every store."},"customers_assigned":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"For an assigned offer list: how many customers are on it today. 0 for any other list."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The order date the entries are read as of."},"entries":{"type":"array","items":{"$ref":"#/components/schemas/PriceListEntry"},"description":"The list's prices in force on `on` or starting after it, by item SKU, then category, pack, quantity break and start."}},"required":["id","code","name","description","kind","audience","is_active","stores","customers_assigned","created_at","updated_at","on","entries"],"additionalProperties":false,"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog."},"PriceListPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PriceListSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of price lists and the cursor for the next."},"CustomerPriceList":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"price_list_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The offer list, whose audience is assigned."},"price_list_code":{"type":"string","description":"The list's code."},"price_list_name":{"type":"string","description":"The list's name."},"valid_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first order date the list prices for them."},"valid_through":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last order date the list prices for them. NULL: until ended. The day before valid_from for a place ended before it began."},"reference":{"description":"The contract or account number the price rests on, for staff and the customer.","type":["string","null"]},"in_force":{"type":"boolean","description":"True when the list prices the customer's orders dated today."}},"required":["id","price_list_id","price_list_code","price_list_name","valid_from","valid_through","reference","in_force"],"additionalProperties":false,"description":"A customer put on an offer list that is only for those assigned to it: a trade or contractor price, an employee price, or a contract. The list prices their orders dated from valid_from through valid_through."},"CustomerPriceLists":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer."},"customer_name":{"type":"string","description":"Their name."},"price_lists":{"type":"array","items":{"$ref":"#/components/schemas/CustomerPriceList"},"description":"Every list they have been put on, newest first, ended ones included."}},"required":["customer_id","customer_name","price_lists"],"additionalProperties":false,"description":"The offer lists a customer is put on (a trade, employee or contract price), which compete with the store's regular prices on their orders."},"ItemPrices":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"Its SKU."},"item_name":{"type":"string","description":"Its name."},"pack_text":{"description":"The pack priced, or null for the item's own unit.","type":["string","null"]},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The quantity priced, in packs for a pack. A decimal string with up to 3 decimal places, for example \"12.500\"."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store."},"location_name":{"type":"string","description":"Its name."},"branch_code":{"type":"string","description":"Its branch code."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer, or null for none."},"customer_name":{"description":"Their name.","type":["string","null"]},"on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The order date priced."},"price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What a line takes: the winning price. Null when nothing prices the item."},"regular_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The store's regular price it competed with. Null when the item has none."},"candidates":{"type":"array","items":{"type":"object","properties":{"price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit of sale (a unit, or a pack) would cost before tax, options not included. A decimal string with up to 2 decimal places, for example \"12.50\"."},"source":{"type":"string","enum":["item","pack","regular_list","offer_list"],"description":"item: the item's own price. pack: the pack's own price. regular_list: the store's regular list. offer_list: an offer list the customer gets here."},"price_list_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The list it comes from. Null for the item's or pack's own price."},"price_list_code":{"description":"That list's code.","type":["string","null"]},"why":{"type":"string","description":"Where it comes from, as a person reads it, for example \"CONTR: 10% off Water Care, 3 or more\"."},"wins":{"type":"boolean","description":"True for the price a line takes: the lowest; a tie goes to the regular price."}},"required":["price","source","price_list_id","price_list_code","why","wins"],"additionalProperties":false},"description":"Every price that could apply, lowest first."}},"required":["item_id","sku","item_name","pack_text","quantity","location_id","location_name","branch_code","customer_id","customer_name","on","price","regular_price","candidates"],"additionalProperties":false,"description":"What one line of an item would cost at a store, for a customer, on a date: every price that could apply and the one a sales order line would take."},"LandedCost":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"purchase_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The purchase order the goods arrived on."},"purchase_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"description":{"type":"string","description":"What was paid for and to whom, for example \"Freight, ABC Carriers bill 5521\"."},"amount":{"type":"string","description":"What was paid, before it is shared out."},"basis":{"type":"string","enum":["value","quantity"],"description":"How it is shared among the arrivals: value (in proportion to what each cost) or quantity (in proportion to how many each brought; a serialized unit is one)."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account credited with the amount."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"applied_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was entered, at the purchase order's location, which is the date of its journal entry. Calculated."},"stock_amount":{"description":"The part added to the cost of stock still on hand. Null to a key without cost.view.","type":["string","null"]},"cost_of_goods_amount":{"description":"The part posted to cost of goods sold, for goods already gone. Null to a key without cost.view.","type":["string","null"]},"shares":{"type":"array","items":{"type":"object","properties":{"purchase_receipt_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The arrival on the purchase order this share is for."},"purchase_receipt_received_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date that arrival came in."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item that arrived."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a serial-tracked item, the unit that arrived. NULL otherwise."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where this part of the share went: where the goods it is on are now (a unit in stock, or quantity stock moved there), or where the goods arrived for the part on goods gone."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"amount":{"type":"string","description":"This part of the arrival's share of the landed cost. The arrival's rows add up to its share."},"stock_amount":{"type":"string","description":"The part added to the cost of stock still on hand, through a landed_cost stock movement."},"cost_of_goods_amount":{"type":"string","description":"The part for goods no longer on hand, posted to cost of goods sold."}},"required":["purchase_receipt_id","purchase_receipt_received_on","item_id","sku","item_name","serialized_unit_id","serial_number","location_id","location_name","amount","stock_amount","cost_of_goods_amount"],"additionalProperties":false},"description":"Each arrival's share and where it went. Empty to a key without cost.view: shares by value tell what each arrival cost."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","purchase_order_id","purchase_order_number","description","amount","basis","ledger_account_id","ledger_account_code","ledger_account_name","applied_on","stock_amount","cost_of_goods_amount","shares","created_at"],"additionalProperties":false,"description":"A cost paid to get goods onto the floor after they arrived (freight, a crane, prep), added to what the goods on one purchase order cost and shared among its arrivals. Rows are only ever added. Its shares are in landed_cost_allocation."},"LandedCostPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LandedCost"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of landed costs and the cursor for the next."},"PurchaseOrderLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item ordered. It must be stock-tracked. Fixed once the line exists."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"tracking":{"type":"string","enum":["none","quantity","serial"],"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},"vendor_sku":{"description":"The vendor's own code for the item. Defaults to the one on record for this vendor.","type":["string","null"]},"description":{"type":"string","description":"What the line says. Defaults to the item's name."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are ordered, in the item's unit. 0 drops the line. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"The cost per unit agreed with the vendor. Defaults to the cost on record for this vendor. On a line ordered by the pack it is pack_cost divided by pack_size, to four places. Calculated. Null without cost.view."},"pack_text":{"description":"For a line ordered in the vendor's pack: the pack, as on the vendor's offer (case, pallet). NULL for a line ordered in the item's own unit. Fixed once the line exists.","type":["string","null"]},"pack_size":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many of the item's unit are in one pack, as the vendor's offer said when the line was written. Calculated. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many of the item's unit are in one pack, as the vendor's offer said when the line was written. Calculated."},"packs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many packs are ordered: quantity is packs times pack_size. Send it instead of quantity to order by the vendor's pack. NULL on a line ordered in units, or whose quantity is not a whole number of packs."},"pack_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"The cost of one pack agreed with the vendor. Defaults to the vendor's pack cost on record, or its unit cost times pack_size. Fixed once goods have arrived on the line. Null without cost.view."},"sales_order_line_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a special order: the customer's order line these goods are being bought for. It must be a line for the same item, on a sales order that is open."},"item_configuration_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a model built with options: how the factory is to build it. Required for a model that has options. On a special order it is the customer's order line's, and follows that line while nothing has arrived. A unit received on the line is recorded as built so."},"options":{"type":"array","items":{"$ref":"#/components/schemas/ChosenOption"},"description":"The options the factory is to build the unit with, group by group. Empty for an item sold as it is."},"options_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the line's options add to the cost of one unit, as they were costed when chosen: part of unit_cost. Calculated. Null without cost.view."},"quantity_received":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many have arrived, less any sent back. Calculated. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_to_receive":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many are still expected: quantity less quantity_received. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_received":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods that arrived cost, at the costs they were received at. Calculated. Null without cost.view."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the vendor says this line's goods will arrive. Starts as the order's expected_on and moves with it while goods are still to come; the vendor can also give this line a date of its own. Every change is kept in expected_date_change. Calculated."},"is_late":{"type":"boolean","description":"True when the order is sent, expected_on has passed at the order's location, and goods are still to come."},"date_changes":{"type":"array","items":{"type":"object","properties":{"previous_expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date expected before this change. NULL when there was none. Calculated."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the vendor now gives. NULL when it gives none."},"note":{"description":"Why the date moved, as the vendor said it, for whoever answers the customer next.","type":["string","null"]},"changed_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it was recorded, at the purchase order's location. Calculated."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who recorded it. NULL when no staff member did (an agent's key acting for nobody)."},"staff_member_name":{"description":"Their name, or null when no staff member did.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["previous_expected_on","expected_on","note","changed_on","staff_member_id","staff_member_name","created_at"],"additionalProperties":false},"description":"Every move of the vendor's date for this line, oldest first."},"waiting_customers":{"type":"array","items":{"$ref":"#/components/schemas/WaitingCustomer"},"description":"Customers' open orders waiting on this line's goods still to come: bought for one (special_order), or next in line for stock on order for the store. Tell them when the date moves (sendMessage)."}},"required":["id","line_number","item_id","sku","item_name","tracking","vendor_sku","description","quantity","unit_cost","pack_text","pack_size","packs","pack_cost","sales_order_line_id","item_configuration_id","options","options_cost","quantity_received","quantity_to_receive","cost_received","expected_on","is_late","date_changes","waiting_customers"],"additionalProperties":false,"description":"One item on a purchase order: how many, at what cost. Lines can change while the order is a draft or ordered. Once any of a line has arrived only its quantity can change, and not below what has arrived."},"PurchaseReceipt":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"purchase_order_line_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The line the goods are for."},"line_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the line on the order, starting at 1. Assigned automatically."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many: positive when received, negative when sent back to the vendor. Exactly 1 or -1 for a serial-tracked item. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"The actual cost per unit, when the vendor's invoice differs from the order. Defaults to the line's cost. Ignored for goods sent back, which are credited at what they were received at. For goods received by the pack: pack_cost divided by the pack size, to four places. Calculated. Null without cost.view."},"packs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"For a line ordered by the pack: how many of its packs arrived (positive) or went back (negative). Send it instead of quantity, which is then packs times the line's pack_size. NULL for goods counted in units."},"pack_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"For goods received by the pack: the actual cost of one pack, when the vendor's invoice differs from the order. Defaults to the line's pack_cost. NULL for goods counted in units and for goods sent back. Null without cost.view."},"extended_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods were taken into stock at. Negative for goods sent back: what they were received at, which is what the vendor credits; for a serialized unit, what that unit was received at. Calculated. Null without cost.view."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the goods arrived at or left from. Goods arriving default to the purchase order's location. Goods sent back default to where they are: a serialized unit's own location, or the location the line's goods arrived at when they all arrived at one, else the purchase order's."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The serialized unit. Send it when a serial-tracked unit goes back to the vendor; the system fills it in when one is received."},"serial_number":{"description":"For a serial-tracked item being received: the serial number of the unit, as on the crate or data plate. The system creates the serialized unit, or takes back one it knows that went to the vendor and has returned.","type":["string","null"]},"stock_lot_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an item tracked by lot: the lot that arrived, or the lot going back to the vendor, which must be named. Goods arriving without one go into the unknown lot, where the tenant allows that."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known.","type":["string","null"]},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the lot expires, as printed on it. NULL when it does not expire or the date is not known. Stock going out without a lot named leaves from the first lot to expire."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who received or sent the goods."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"received_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it happened, at the location. Calculated."},"vendor_rma_number":{"description":"For goods sent back: the vendor's return authorization (RMA) number, as the vendor gave it, to match their credit memo against. NULL when none was given, and on goods received.","type":["string","null"]},"vendor_credit_expected":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For goods sent back: what the vendor said it will credit for them, when that is known: less than extended_cost when it keeps a restocking fee. Recorded for whoever enters the vendor's credit memo in the accounting system; nothing posts from it, and what is owed comes down by what the goods were received at. NULL when not given, and on goods received. Null without cost.view."},"written_off_stock_movement_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For goods sent back that were already taken out of stock as a loss (a case damaged in freight, written off when it arrived): that loss, an adjustment out of the same item at the location they go back from. The goods are put back on the books first, undoing that much of the loss (restored_stock_movement_id), so they leave stock once, as a return to the vendor. A loss is sent back at most up to what it took out. A serial-tracked unit written off is found by the system. NULL for goods still in stock."},"restored_stock_movement_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The adjustment that put written-off goods back on the books, at what the loss took them out at and under its reason, just before they went back to the vendor. Set by the system with written_off_stock_movement_id."},"note":{"description":"Anything worth recording: damage on arrival, why goods went back.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","purchase_order_line_id","line_number","sku","quantity","unit_cost","packs","pack_cost","extended_cost","location_id","location_name","serialized_unit_id","serial_number","stock_lot_id","lot_code","expires_on","staff_member_id","staff_member_name","received_on","vendor_rma_number","vendor_credit_expected","written_off_stock_movement_id","restored_stock_movement_id","note","created_at"],"additionalProperties":false,"description":"Goods on one purchase order line arriving from the vendor, or going back to them. Rows are only ever added; a return to the vendor is a new row with a negative quantity. Each row takes the stock in or out through the stock ledger."},"PurchaseOrderSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"purchase_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["draft","ordered","closed","cancelled"],"description":"draft: being put together. ordered: sent to the vendor; goods can be received. closed: nothing more is expected; the system closes it when every line is received in full, and it can be closed by hand when the vendor will not send the rest. cancelled: never sent, or withdrawn before anything arrived. Cancelled is final. Closed is final for the application, but goods can still be sent back, and sending goods back on an order that had arrived in full reopens it as ordered so the vendor's replacement can be received; close it by hand if the vendor credits them instead."},"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor ordered from. Fixed once the order has lines."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the goods are to be delivered to."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"ordered_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the order was sent to the vendor. Set automatically when it becomes ordered."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the vendor says the goods will arrive."},"notes":{"description":"Free-form notes for staff.","type":["string","null"]},"total_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the order comes to at its lines' costs. Null without cost.view."},"is_late":{"type":"boolean","description":"True when a line's expected_on has passed with goods still to come on the sent order."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","purchase_order_number","status","vendor_id","vendor_name","location_id","location_name","ordered_on","expected_on","notes","total_cost","is_late","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines. As listed: getPurchaseOrder adds lines and receipts."},"PurchaseOrder":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"purchase_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number quoted to the vendor, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["draft","ordered","closed","cancelled"],"description":"draft: being put together. ordered: sent to the vendor; goods can be received. closed: nothing more is expected; the system closes it when every line is received in full, and it can be closed by hand when the vendor will not send the rest. cancelled: never sent, or withdrawn before anything arrived. Cancelled is final. Closed is final for the application, but goods can still be sent back, and sending goods back on an order that had arrived in full reopens it as ordered so the vendor's replacement can be received; close it by hand if the vendor credits them instead."},"vendor_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vendor ordered from. Fixed once the order has lines."},"vendor_name":{"type":"string","description":"The vendor's name, unique within the tenant."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the goods are to be delivered to."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"ordered_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the order was sent to the vendor. Set automatically when it becomes ordered."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the vendor says the goods will arrive."},"notes":{"description":"Free-form notes for staff.","type":["string","null"]},"total_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the order comes to at its lines' costs. Null without cost.view."},"is_late":{"type":"boolean","description":"True when a line's expected_on has passed with goods still to come on the sent order."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/PurchaseOrderLine"},"description":"The lines, in line_number order. Dropped lines have quantity 0."},"receipts":{"type":"array","items":{"$ref":"#/components/schemas/PurchaseReceipt"},"description":"Every arrival and return to the vendor, oldest first."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","purchase_order_number","status","vendor_id","vendor_name","location_id","location_name","ordered_on","expected_on","notes","total_cost","is_late","custom_fields","created_at","updated_at","lines","receipts","next"],"additionalProperties":false,"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines."},"PurchaseOrderPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PurchaseOrderSummary"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of purchase orders and the cursor for the next."},"ReceiptlessReturn":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"return_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"returned_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who brought the goods back. Needed for a refund in store credit; NULL for an anonymous walk-in refunded by another method."},"customer_name":{"description":"The customer's name, or null for an anonymous walk-in.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the goods came back. They come back into stock here, and the revenue and tax are taken back here."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"What came back. A good tracked by quantity or not tracked; a serial-tracked unit goes back on the order it was sold on."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many came back, in the item's unit. A decimal string with up to 3 decimal places, for example \"12.500\"."},"restock":{"type":"boolean","description":"True when the goods go back into stock to sell again, at the location's average cost. False when they are thrown away or unsellable: nothing comes into stock and the cost of the original sale stands. Always false for an item whose stock is not tracked."},"stock_lot_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For an item tracked by lot, restocked: the lot of the goods, as printed on them. No sale says which lot they left from, so they come in as stock arriving does: left out, into the unknown lot, and only where the tenant allows that."},"lot_code":{"description":"The vendor's lot or batch code as printed on the goods, uppercase, unique within the item. NULL for the item's unknown lot: stock whose lot is not known. Of stock_lot_id.","type":["string","null"]},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What one unit was valued at, by the business's policy: the item's price now, or the lowest it recently sold for if lower. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"revenue_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Revenue taken back: quantity times unit_price, negative as for a return on an order. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax given back, negative. 0 unless the business refunds tax on these returns. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cost of the goods that came back into stock, negative as for a return on an order. 0 when nothing was restocked. Calculated. Null without cost.view."},"refund_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the customer was given: the value and the tax given back, positive. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"How the refund went out: a payment method, or the Store credit method for credit to the customer. Defaults to Store credit when the business gives only store credit for these returns."},"payment_method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"reference":{"description":"For a refund by check: the number of the check the business wrote, so the bank statement can be matched. Needed while the business requires check numbers (tenant_policy.check_number_required). A note of the method's own reference otherwise, or NULL.","type":["string","null"]},"cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer the cash refund came out of, for a refund by a cash method. Needed for cash when the business keeps every dollar in a drawer (tenant_policy.cash_needs_drawer). NULL for any other method. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The drawer's number, or null when none was named. Null as well when the business counts blind (cash_count_blind) and this API key holds neither cash.manage nor cost.view at the location."},"store_credit_balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The customer's store credit balance after this return, when the refund went to store credit. Null otherwise."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took the goods back."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"note":{"type":"string","description":"Why the goods came back and why no sale could be found for them. Required."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","return_number","returned_on","customer_id","customer_name","location_id","location_name","item_id","sku","item_name","quantity","restock","stock_lot_id","lot_code","unit_price","revenue_amount","tax_amount","cost_amount","refund_amount","payment_method_id","payment_method_name","reference","cash_drawer_session_id","cash_drawer_session_number","store_credit_balance","staff_member_id","staff_member_name","note","created_at"],"additionalProperties":false,"description":"Goods a customer brought back that were not sold on an order the system can find, and what they were given for them. Revenue, tax and cost are taken back as for a return on an order, and the refund goes out in the same step. Rows are only ever added. Allowed only as far as the business's policy says (tenant_policy.receiptless_returns)."},"Receivable":{"type":"object","properties":{"kind":{"type":"string","enum":["order","on_account","finance_charge"],"description":"order: money owed on an order for goods handed over (sales_order_id). on_account: a part of what the customer owes on account with no order behind it, left by a payment on account that did not arrive after its money had gone back to them (account_payment_id), or by a gift card they bought whose sale was reversed after some of it was spent (gift_card_id). finance_charge: a charge for paying late (finance_charge_id), owed on account too. takeAccountPayment collects what is owed on account, before any order."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order owed on. NULL for what is owed on account."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number. NULL for what is owed on account."},"account_payment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The reversal (a payment on account that did not arrive) that started this part. NULL for a part a gift card or a finance charge started. NULL for what is owed on an order."},"gift_card_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a part a gift card sale reversed started: the card. NULL otherwise."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The gift card's number, or null."},"finance_charge_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a part a finance charge started: the charge. NULL otherwise."},"finance_charge_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The finance charge's number, or null."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who owes it."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location that made the sale."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is owed on the order: handed over less paid. A decimal string with up to 2 decimal places, for example \"12.50\"."},"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"When payment falls due: the date of the oldest hand-over that payments have not yet covered, plus the payment terms the customer had when the order first came to be owed on (a later change to their terms does not move it). Goods brought back cancel the hand-overs of their own line, so a returned hand-over does not set it; a restocking fee kept on a return counts as handed over on the day of the return, and a cancellation fee on the day of the cancellation. What is owed beyond everything charged (money refunded, then the payment it came from reversed) is owed from the day the order's payments went below nothing. A later hand-over does not move it."},"days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days past due_on it is today, at the location that made the sale. 0 when it is not yet due."}},"required":["kind","sales_order_id","order_number","account_payment_id","gift_card_id","gift_card_number","finance_charge_id","finance_charge_number","customer_id","customer_name","location_id","location_name","amount_due","due_on","days_overdue"],"additionalProperties":false,"description":"One thing a customer owes: an order with goods handed over and not yet paid for, or a part of what they owe on account. Together they add up to the Accounts Receivable account."},"ReceivablePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Receivable"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of what customers owe."},"CustomerBalance":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The customer's credit limit, for comparison. NULL is no limit."},"payment_terms_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days after goods are handed over payment falls due. 0 is due on hand-over; 30 is \"net 30\"."},"amount_due":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Everything the customer owes: for goods handed over, and on account (owed_on_account). A decimal string with up to 2 decimal places, for example \"12.50\"."},"amount_overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of amount_due that is past its due date. A decimal string with up to 2 decimal places, for example \"12.50\"."},"oldest_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The earliest due date among what is owed, on an order or on account. NULL when nothing is owed."},"deposits_held":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money held for the customer across their orders: paid for goods not yet handed over, or owed back for goods returned or called off and not yet refunded. It is what can be refunded. A decimal string with up to 2 decimal places, for example \"12.50\"."},"account_credit":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Money the customer paid on account and not yet applied to an order. amount_due does not count it: apply it with applyAccountCredit, or give it back with refundAccountCredit. Never below zero. A decimal string with up to 2 decimal places, for example \"12.50\"."},"owed_on_account":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of amount_due owed on account with no order behind it: a payment on account that did not arrive after its money had gone back to them as money, or a finance charge. takeAccountPayment collects it before anything else; it falls due like an order (amount_overdue, oldest_due_on), and the credit limit and overdue holds count it. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["customer_id","customer_name","customer_number","credit_limit","payment_terms_days","amount_due","amount_overdue","oldest_due_on","deposits_held","account_credit","owed_on_account"],"additionalProperties":false,"description":"One row per customer: what they owe, how much of it is late, and what the tenant holds of theirs: deposits, money owed back for goods returned, store credit and money paid on account not yet applied. Not stored: read from the orders, so it is always current. It is what a statement is drawn from. Orders with no customer are not in it: an anonymous sale is paid and handed over in one go."},"CustomerBalancePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerBalance"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of customer balances."},"CustomerLiability":{"type":"object","properties":{"kind":{"type":"string","enum":["gift_cards","store_credit","account_credit","deposits"],"description":"gift_cards: what gift cards hold. store_credit: store credit customers can spend. account_credit: money customers paid on account and not yet applied to an order. deposits: what customers paid on orders beyond what has been handed over."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The control account it is kept on."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"outstanding":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is owed now, added up from the cards, credit balances or orders. A decimal string with up to 2 decimal places, for example \"12.50\"."},"ledger_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The control account's balance now (credits less debits): equal to outstanding. A decimal string with up to 2 decimal places, for example \"12.50\"."},"is_tied":{"type":"boolean","description":"Whether outstanding equals ledger_balance. Always true while the books are consistent (getBooksCheck)."},"opening":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The control account's balance at the end of the day before from. A decimal string with up to 2 decimal places, for example \"12.50\"."},"added":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What customers were given or paid from from to to that the business still owed them when the step that did it was over: money that passed straight through in one step (a counter sale's payment, used by its hand-over; a payment on account applied to orders as it was taken) is not counted. Reversals are apart. A decimal string with up to 2 decimal places, for example \"12.50\"."},"taken_off":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What was spent, given back, applied or written off from from to to, counted the same way as added. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reversed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What was taken back from from to to because the step that put it there was undone (a bounced check, a chargeback, a reversed entry), net: positive when it lowered what is owed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closing":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Its balance at the end of to: opening plus added less taken_off less reversed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"holder":{"type":"string","enum":["card","customer","order"],"description":"What holders counts: gift cards, customers or orders."},"holders":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many hold something now."},"oldest_activity_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The earliest last activity among the holders: the longest anything owed has sat untouched. Null when nothing is owed."},"dormant_amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is owed to holders with no activity for dormant_days or more. A decimal string with up to 2 decimal places, for example \"12.50\"."},"dormant_holders":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many holders that is."}},"required":["kind","ledger_account_id","ledger_account_code","ledger_account_name","outstanding","ledger_balance","is_tied","opening","added","taken_off","reversed","closing","holder","holders","oldest_activity_on","dormant_amount","dormant_holders"],"additionalProperties":false,"description":"One kind of what the business owes its customers, tied to its account."},"CustomerLiabilities":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today where the business is: outstanding and dormancy are as of now."},"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the period moved through."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day of the period."},"dormant_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The days without activity after which a holder counts as dormant."},"blocks":{"type":"array","items":{"$ref":"#/components/schemas/CustomerLiability"},"description":"gift_cards, store_credit, account_credit and deposits, in that order."},"total_outstanding":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Everything the business owes its customers now: the blocks added up. A decimal string with up to 2 decimal places, for example \"12.50\"."},"gift_cards_by_status":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["active","blocked","closed"],"description":"active: it can be spent and reloaded. blocked: reported lost or stolen, or held for a dispute; nothing can be spent from it until it is unblocked or replaced, and what it holds is still owed. closed: replaced, written off, cashed out or its sale voided; it holds nothing and is never used again."},"cards":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many cards have that status."},"balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What they hold. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["status","cards","balance"],"additionalProperties":false},"description":"Every gift card by status."},"gift_card_breakage_after_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many days a card must go unused before what it holds may be written off as breakage, taken into income as never to be spent (writeOffGiftCard). NULL (the default) takes no breakage: the balance stays owed until it is spent or written off as unclaimed property. Whether and when breakage may be taken is a question for the business's accountant."},"breakage_eligible_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What cards unused for gift_card_breakage_after_days hold, which writeOffGiftCard may take as breakage. Null when the business takes no breakage."},"breakage_eligible_cards":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many cards that is. Null when the business takes no breakage."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["as_of","from","to","dormant_days","blocks","total_outstanding","gift_cards_by_status","gift_card_breakage_after_days","breakage_eligible_amount","breakage_eligible_cards","next"],"additionalProperties":false,"description":"What the business owes its customers in gift cards, store credit, account credit and deposits."},"UsualItem":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"times_bought":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"On how many days in the last two years they bought it."},"last_quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"How many they took the last time. A decimal string with up to 3 decimal places, for example \"12.500\"."},"last_unit_price":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What each cost them the last time, before tax. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["item_id","sku","item_name","times_bought","last_quantity","last_unit_price"],"additionalProperties":false,"description":"Something a customer usually buys, with what they took last time."},"DueContact":{"type":"object","properties":{"contact_point_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The contact point, for sendMessage."},"contact_point_kind":{"type":"string","enum":["email","telephone"],"description":"email or telephone."},"contact_point_value":{"type":"string","description":"The address or number itself: a lowercase email address, or a phone number in E.164 form such as +12085550100."},"marketing_allowed":{"type":"boolean","description":"Whether a marketing message may go to it: only when its marketing consent is granted."},"service_allowed":{"type":"boolean","description":"Whether a service message may go to it: unless its service consent is denied."}},"required":["contact_point_id","contact_point_kind","contact_point_value","marketing_allowed","service_allowed"],"additionalProperties":false,"description":"An active way to reach a customer, and what their consent allows."},"CustomerDue":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"home_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Their home store: where they last bought it."},"home_location_name":{"type":"string","description":"Its name."},"home_branch_code":{"type":"string","description":"Its branch code."},"purchases":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Purchase days: on how many different days in the last two years they bought something asked about and kept it. Several items bought on one day are one purchase; the same item bought on two days is two."},"due_item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item they are due for first, which the dates below are about."},"due_item_sku":{"type":"string","description":"Its SKU."},"due_item_name":{"type":"string","description":"Its name."},"last_purchased_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day they bought that item."},"interval_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days they usually go between purchases of that item."},"interval_basis":{"type":"string","enum":["their_purchases","category"],"description":"their_purchases: the median gap between the days they bought the item. category: they bought it once, so its category's repurchase_days."},"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"When they are due to buy it again: last_purchased_on plus interval_days."},"days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days past due_on at their home store: 0 when not yet due."},"due_status":{"type":"string","enum":["not_due","due_soon","overdue","lapsed"],"description":"not_due: due later than due_within_days from today. due_soon: due within due_within_days, or today. overdue: past due_on. lapsed: past lapsed_after intervals since they last bought it."},"usual_items":{"type":"array","items":{"$ref":"#/components/schemas/UsualItem"},"description":"Up to three items they buy most often, most often first."},"spend_12_months":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What they spent on the items asked about in the last 365 days, before tax, less what they brought back in that time (which can come to less than 0 when goods bought earlier came back). A decimal string with up to 2 decimal places, for example \"12.50\"."},"has_subscription":{"type":"boolean","description":"Whether an active subscription already sends or brings them one of these items."},"owns_equipment":{"type":"boolean","description":"Whether they have equipment on record."},"reachable_for_marketing":{"type":"boolean","description":"Whether any contact point allows a marketing message."},"reachable_for_service":{"type":"boolean","description":"Whether any contact point allows a service message."},"contact_points":{"type":"array","items":{"$ref":"#/components/schemas/DueContact"},"description":"Their active contact points, primary first."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["customer_id","customer_name","customer_number","home_location_id","home_location_name","home_branch_code","purchases","due_item_id","due_item_sku","due_item_name","last_purchased_on","interval_days","interval_basis","due_on","days_overdue","due_status","usual_items","spend_12_months","has_subscription","owns_equipment","reachable_for_marketing","reachable_for_service","contact_points","next"],"additionalProperties":false,"description":"A customer due to buy something again, and how to reach them."},"CustomersDue":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today at location_id, or where the business is."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category asked about, with every category below it."},"category_name":{"description":"Its name.","type":["string","null"]},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item asked about."},"item_name":{"description":"Its name.","type":["string","null"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerDue"},"description":"The customers, soonest due first, up to limit."},"next_cursor":{"description":"Send as cursor for the customers after these. Null when there are no more.","type":["string","null"]},"counts":{"type":"object","properties":{"not_due":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers not yet due."},"due_soon":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers due within due_within_days."},"overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers past due."},"lapsed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Customers past lapsed_after intervals."}},"required":["not_due","due_soon","overdue","lapsed"],"additionalProperties":false,"description":"Every customer who has bought it, by status, whatever status asked for."},"buyers_prior_year":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Active customers who bought something asked about more than 365 and less than 730 days ago."},"returned_this_year":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of them bought it again in the last 365 days."},"repeat_percent":{"description":"returned_this_year as a percent of buyers_prior_year, to one decimal place. Null when there were none.","type":["string","null"]},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["as_of","category_id","category_name","item_id","item_name","data","next_cursor","counts","buyers_prior_year","returned_this_year","repeat_percent","next"],"additionalProperties":false,"description":"Customers due to buy something again, from what they bought and how often."},"ReportGroup":{"type":"object","properties":{"dimension":{"type":"string","enum":["day","week","month","quarter","year","location","category","top_category","brand","item","salesperson","channel","customer","order","tracking"],"description":"What the row is grouped by."},"kind":{"type":"string","enum":["record","period","value","none","unattributed","everyone_else"],"description":"record: one location, category, brand, item, salesperson, channel, customer or order, named by id. period: a day, week, month, fiscal quarter or fiscal year, from period_start to period_end. value: a tracking value. none: sales with nothing to group by (no category, no brand, no channel, or an anonymous customer). unattributed: the part of sales no salesperson is credited with, including goods taken back without a receipt. everyone_else: every salesperson but the key's own, for a key without commission.view."},"id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The record's id, for kind record. Null otherwise."},"code":{"description":"What people quote it by: a branch code, a SKU, a customer number or an order number. Null when it has none.","type":["string","null"]},"name":{"type":"string","description":"What to call it: a name, the period, or a tracking value."},"period_start":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For kind period, its first day. Null otherwise."},"period_end":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"For kind period, its last day. Null otherwise."}},"required":["dimension","kind","id","code","name","period_start","period_end"],"additionalProperties":false,"description":"What one row of a report counts: a record, a period or a value."},"DeliveredSales":{"type":"object","properties":{"hand_overs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Hand-overs counted."},"returns":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Returns counted, with or without a receipt."},"orders":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Orders with a hand-over counted: tickets."},"quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units handed over less units returned. Meaningful for one item. A decimal string with up to 3 decimal places, for example \"12.500\"."},"gross_revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue before tax of the hand-overs. A decimal string with up to 2 decimal places, for example \"12.50\"."},"returned_revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue given back on returns, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"gross_revenue less returned_revenue: what the income account carries for it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Sales tax charged, net of tax given back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods cost, net of goods that came back into stock. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"margin":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"revenue less cost: the margin on goods sold. Sales allowances (goodwill store credit, credits that bring no goods back) are not sales and are not taken off, so the books' gross profit (getFinancialStatement) is lower than this by them. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"margin_percent":{"description":"margin as a percent of revenue, to one decimal place. Null when revenue is 0. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations).","type":["string","null"]},"average_ticket":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"gross_revenue divided by orders, to the cent. Null when there are no orders."}},"required":["hand_overs","returns","orders","quantity","gross_revenue","returned_revenue","revenue","tax","cost","margin","margin_percent","average_ticket"],"additionalProperties":false,"description":"Sales counted when the goods changed hands (basis delivered): what the books recognize as income, dated each hand-over and return."},"BookedSales":{"type":"object","properties":{"orders_booked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Orders opened in the period: the customer agreed."},"orders_cancelled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Opened orders cancelled in the period."},"booked":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the orders opened came to before tax, as they stand now, with any goods since returned added back. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cancelled":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the orders cancelled still had to hand over, before tax, when they were cancelled. A decimal string with up to 2 decimal places, for example \"12.50\"."},"net_booked":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"booked less cancelled. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["orders_booked","orders_cancelled","booked","cancelled","net_booked"],"additionalProperties":false,"description":"Sales counted when the customer agreed (basis booked): orders opened, dated opened_on, less what cancelled orders had not handed over, dated cancelled_on. Quotes never count."},"SalesComparison":{"type":"object","properties":{"prior":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The same figure in the compared period: revenue for delivered, net_booked for booked."},"change":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"This period's figure less prior. A decimal string with up to 2 decimal places, for example \"12.50\"."},"change_percent":{"description":"change as a percent of prior, to one decimal place. Null when prior is 0.","type":["string","null"]},"prior_margin":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For delivered, margin in the compared period. Null for booked. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"margin_change":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"For delivered, margin less prior_margin. Null for booked. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."}},"required":["prior","change","change_percent","prior_margin","margin_change"],"additionalProperties":false,"description":"One row's figure set against the compared period."},"SalesReportRow":{"type":"object","properties":{"group":{"anyOf":[{"$ref":"#/components/schemas/ReportGroup"},{"type":"null"}],"description":"What the row counts. Null for the totals and the other row."},"subgroup":{"anyOf":[{"$ref":"#/components/schemas/ReportGroup"},{"type":"null"}],"description":"Within group, what the row counts when then_by is sent. Null otherwise."},"delivered":{"anyOf":[{"$ref":"#/components/schemas/DeliveredSales"},{"type":"null"}],"description":"The figures, for basis delivered. Null for booked."},"booked":{"anyOf":[{"$ref":"#/components/schemas/BookedSales"},{"type":"null"}],"description":"The figures, for basis booked. Null for delivered."},"comparison":{"anyOf":[{"$ref":"#/components/schemas/SalesComparison"},{"type":"null"}],"description":"Set against the compared period. Null when compare is none."}},"required":["group","subgroup","delivered","booked","comparison"],"additionalProperties":false,"description":"The figures for one group, or for all of them."},"SalesBacklog":{"type":"object","properties":{"open_orders":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open orders with something still to hand over, as of now."},"to_hand_over":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is still to be handed over on them, before tax: sold but not delivered. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposits_held":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What customers have paid on them beyond what was handed over: deposits on goods still to come. Null when the report is filtered to items, categories, brands or tracking, which split orders."}},"required":["open_orders","to_hand_over","deposits_held"],"additionalProperties":false,"description":"Sold but not yet delivered, as of now whatever the period, under the same filters."},"SalesReport":{"type":"object","properties":{"basis":{"type":"string","enum":["delivered","booked"],"description":"How sales were counted."},"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day counted."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day counted."},"compare":{"type":"string","enum":["none","prior_period","prior_week","prior_year","prior_year_weekday"],"description":"What the figures are set against."},"prior_from":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The compared period's first day. Null when compare is none."},"prior_to":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The compared period's last day. Null when compare is none."},"group_by":{"anyOf":[{"type":"string","enum":["day","week","month","quarter","year","location","category","top_category","brand","item","salesperson","channel","customer","order","tracking"]},{"type":"null"}],"description":"What rows are grouped by. Null for totals only."},"then_by":{"anyOf":[{"type":"string","enum":["day","week","month","quarter","year","location","category","top_category","brand","item","salesperson","channel","customer","order","tracking"]},{"type":"null"}],"description":"What each group is split by. Null when not split."},"books_closed_through":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last date of the closed accounting period. Nothing can be posted on or before it, so figures already reported or exported cannot change. NULL when no period is closed."},"is_final":{"type":"boolean","description":"True when the books are closed through `to`, so nothing more can be posted in the period."},"groups":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many groups there are in all: rows beyond limit are added up in other."},"rows":{"type":"array","items":{"$ref":"#/components/schemas/SalesReportRow"},"description":"One per group, up to limit: periods in date order, anything else by revenue (net_booked for booked), largest first. With then_by, grouped under each group the same way."},"other":{"anyOf":[{"$ref":"#/components/schemas/SalesReportRow"},{"type":"null"}],"description":"Every group past limit, added up. Null when every group is in rows."},"totals":{"description":"Every sale counted, whatever the grouping. Money and quantities in rows plus other add up to these exactly; a sale split between salespeople is shared by its credit shares, each share rounded so the shares add up to the sale. Counts (hand_overs, returns, orders, orders_booked, orders_cancelled) and average_ticket do not add up: one order, or one hand-over split between salespeople, is counted once in each row it falls in and once here.","$ref":"#/components/schemas/SalesReportRow"},"backlog":{"anyOf":[{"$ref":"#/components/schemas/SalesBacklog"},{"type":"null"}],"description":"For basis booked, what is sold and not delivered. Null for delivered."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["basis","from","to","compare","prior_from","prior_to","group_by","then_by","books_closed_through","is_final","groups","rows","other","totals","backlog","next"],"additionalProperties":false,"description":"Sales for a period, grouped as asked, with totals and what they are set against."},"PaymentMethod":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"kind":{"type":"string","enum":["cash","check","card","financing","other","store_credit","account_credit","gift_card","trade_in"],"description":"What sort of method it is: cash, check, card, financing (a lender pays the tenant), other, store_credit, account_credit or gift_card. store_credit is the one method the system makes for paying with a customer's store credit and refunding into it: it posts to the Store Credit account and moves the customer's balance. account_credit (\"On account\") is the one the system makes for paying an order out of money the customer paid on account, and for moving money held on an order back into their account credit: it posts to Customer Payments on Account. gift_card is the one the system makes for paying with a gift card and refunding onto one, naming the card: it posts to Gift Cards Outstanding and moves the card's balance. trade_in (\"Trade-in\") is the one the system makes for what a customer is allowed for an old unit they trade in toward an order: it posts to Trade-Ins Clearing, and only receiving or giving back the trade (receiveTradeIn, giveBackTradeIn) pays or refunds by it. None of them can be added, and no other method can become one."},"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account a payment by this method is debited to, for example Undeposited Funds for cash, or a clearing account for a card processor. Not a control account."},"is_active":{"type":"boolean","description":"False once the method is no longer offered."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"financing_lender_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"For a lender's own payment method: the lender. Made with the lender and kept by the system: it posts to Lender Receivable, and is renamed and withdrawn with the lender. NULL for every other method."},"financing_lender_name":{"description":"The lender's name, or null for a method that is not a lender's.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","kind","ledger_account_id","is_active","ledger_account_code","ledger_account_name","financing_lender_id","financing_lender_name","created_at","updated_at"],"additionalProperties":false,"description":"A way customers pay the tenant, for example Cash, \"Card (Stripe)\" or \"Wells Fargo financing\". Each names the account its money lands in, so the books show where every payment went. A new tenant has Cash, Check, Card, Store credit, On account, Gift card and Trade-in."},"PaymentMethodPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of payment methods."},"Policy":{"type":"object","properties":{"inventory_costing_method":{"type":"string","enum":["average","fifo"],"description":"How the cost of stock leaving is worked out: average (running average cost of what is on hand) or fifo (oldest receipts first). It should match the method the business reports to its accountant. Fixed once any stock has moved."},"allow_negative_stock":{"type":"boolean","description":"True to let a quantity-tracked item be issued when the system shows too little on hand, so a sale is never blocked by late paperwork. False to refuse it. Either way a transfer or a return to a vendor carries only what the location holds, and serialized units can never go negative."},"unknown_lot_allowed":{"type":"boolean","description":"For items tracked by lot: true to let stock come in (received, opening stock, an adjustment or a count) without naming its lot, into the item's unknown lot. False to refuse it, so every unit on the shelf can be traced in a recall. Goods coming back (a customer's return, a transfer) always come back into the lot they left from."},"stock_count_blind":{"type":"boolean","description":"True (the default): whoever counts stock is not shown what the system holds of each item, nor how far over or short their count is, until the count is posted, so they count what is there rather than confirm a figure. Someone holding purchase.manage at the location may choose to see those figures: they read what is on hand to decide what to buy, so hiding it from them hides nothing. False: everyone counting sees them. The API applies it to the count itself: while a blind count is open, getStockCount leaves out what the system held of each item and which serialized units it expects, unless the key holds purchase.manage at the location. What is on hand stays readable through listStock, which selling needs."},"stock_count_missing_units":{"type":"string","enum":["flag","write_off"],"description":"What posting a stock count that covers serialized units does with a unit recorded at the location that was not scanned. flag (the default): the unit stays in stock and is marked missing on the count, for someone to find: a spa not scanned is far more often on a truck, at another store or behind the building than gone, and writing it off moves its whole cost to inventory adjustments. write_off: the unit leaves stock at its cost, as a count's shortage does. A unit an order or quote holds for a customer is only ever flagged: it may be out for delivery."},"par_level_replenishment":{"type":"string","enum":["with_pace","par_only"],"description":"How getReplenishment weighs an item's par level at a location against how fast it sells there. with_pace (the default): it suggests whichever asks for more, so a slow mover that must always be on the shelf is brought back up to par when it falls to its minimum, and a fast seller is still ordered for the days ahead when its pace outruns the par. par_only: where an item has a par level, the par level alone decides, so a buyer who has set the shelf's levels is never asked for more. Items with no par level are ordered by pace either way. A vehicle is always restocked to its par levels alone."},"books_closed_through":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last date of the closed accounting period. Nothing can be posted on or before it, so figures already reported or exported cannot change. NULL when no period is closed."},"new_customer_credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The tenant's choice of the credit limit a new customer starts with when none is given. NULL (the default) starts them with no limit, as before limits existed; a figure such as 0, which has everything paid for before it is handed over, makes credit something staff grant. Starting a customer with more, or with none when this is set, needs credit.manage."},"tax_rounding":{"type":"string","enum":["line","order"],"description":"The tenant's choice of how sales tax is rounded to the cent, for each jurisdiction. line (the default): each line's tax is rounded on its own and the order's tax is their sum. order: the tax on the whole order is rounded once, and shared out to its lines so that they add up to it; with prices like 9.99 this can come to a cent less than rounding each line. Use what the tenant's states require or allow. An order keeps the rounding in force when it was written up (sales_order.tax_rounding)."},"tax_rate_date":{"type":"string","enum":["order_date","hand_over"],"description":"The tenant's choice of which day's tax rates a sale is charged at, which each state's law decides. order_date (the default): the rates in force on the order's date, so a tub ordered before a rate change and delivered after it keeps the old rate. hand_over: the rates in force on the day the goods are handed over; until then an order shows today's rates, and reading the order, a hand-over or a payment brings the lines not yet handed over to that day's rates, so a deposit taken against the old total is weighed against the new one. A line partly handed over keeps the rates of its first hand-over for the rest of it. An order keeps the choice in force when it was written up (sales_order.tax_rate_date)."},"carry_out_elsewhere":{"type":"string","enum":["refuse","tax_at_pickup"],"description":"The tenant's choice of what happens when goods the customer carries out (an order with no delivery site) are handed over at a location whose tax area would tax the order differently from the order's own. refuse (the default): the hand-over is refused, and the goods are brought to a location that taxes them the same, or the order is moved. tax_at_pickup: the order is taxed again in the tax area of the location handing it over, as long as none of it has yet been handed over; only from a location that holds the goods handed over, and never for a service, which is not carried out of anywhere; until then its tax area may be moved to a location's tax area without tax.override when that does not lower the tax, and what was already paid stays as a deposit against the new total. Areas that charge the same jurisdictions at the same rates on the order's items count as the same either way."},"exemption_without_certificate":{"type":"string","enum":["charge_tax","exempt"],"description":"What happens when a tax-exempt customer buys and no certificate of theirs covers the sale (none on file, expired, not yet in force, or good in another state). charge_tax (the default): the sale is taxed, and says why, until a certificate that covers it is recorded or someone holding tax.override exempts it. exempt: the sale is untaxed anyway, naming no certificate; listTaxExemptionCertificates shows whose to chase."},"new_customer_payment_terms_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The payment terms a new customer starts with when none are given, in days after hand-over: 0 is due on hand-over, 30 is \"net 30\". Changing it, or starting a customer on longer terms, needs credit.manage."},"hold_when_overdue_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Holds goods and service on account from a customer who has anything more than this many days overdue: a hand-over that would add to what they owe is refused, while one paid for in full still goes through. 0 holds from the first day overdue. NULL holds nobody; the credit limit alone applies. Changing it needs credit.manage."},"finance_charges":{"type":"boolean","description":"True when the business charges customers for paying late: assessFinanceCharges then charges each customer, once a month, on what they owe past due. False by default, so nobody is charged until the business decides to, at a rate (finance_charge_monthly_percent) or a flat amount (finance_charge_minimum) it chooses. Charges already made are still owed when it is turned off. Changing it needs credit.manage."},"finance_charge_monthly_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The finance charge, as a percent a month of what is past due: 1.5 is 18% a year. It is charged by the day, at this times 12 over 365 for each day an amount was past due in the month. 0 (the default) charges no rate: only finance_charge_minimum, a flat late fee. At most 10. Changing it needs credit.manage. A decimal string with up to 3 decimal places, for example \"12.500\"."},"finance_charge_minimum":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least a customer is charged in a month when they are charged at all: with a rate, the larger of the two; with no rate, a flat monthly late fee. 0 (the default) charges no minimum. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"finance_charge_min_overdue":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A customer whose past-due amounts charged on add up to less than this is not charged that month, so a few dollars late never brings a minimum charge larger than itself. 0 (the default) charges any amount past due. Changing it needs credit.manage. A decimal string with up to 2 decimal places, for example \"12.50\"."},"finance_charge_grace_days":{"type":"integer","minimum":0,"maximum":365,"description":"How many days past due an amount must be, at the month's end, before it is charged on. 0 (the default) charges anything past due; 10 leaves an invoice 10 days late or less alone that month. Changing it needs credit.manage."},"finance_charge_compounds":{"type":"boolean","description":"True when an earlier finance charge still unpaid past its due date is charged on as well, as any other amount past due. False (the default): finance charges are never charged on. Changing it needs credit.manage."},"store_credit_offered":{"type":"boolean","description":"True when the business gives store credit: refunds to the Store credit payment method, goodwill credits, and returns without a receipt refunded as credit. False by default, so nothing becomes owed as credit until the business decides it should. Credit already given can always be spent, and a refund can always put back what an order was paid in store credit, whatever this says."},"receiptless_returns":{"type":"string","enum":["refused","store_credit","any_tender"],"description":"Whether goods are taken back when no sale for them can be found. refused (the default): never; the goods go back only on the order they were sold on. store_credit: taken back for store credit only, which needs store_credit_offered and a named customer. any_tender: refunded by any payment method, store credit included."},"receiptless_return_price_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":730},{"type":"null"}],"description":"What goods taken back without a receipt are worth, per unit: the lowest the item sold for, after discounts, in this many days before the return, when that is below its price now. Goods given away for nothing do not count. NULL values them at the item's price now."},"receiptless_return_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most one return without a receipt can give back, tax included. NULL sets no limit."},"receiptless_return_refunds_tax":{"type":"boolean","description":"True to give back sales tax on goods taken back without a receipt, at the location's rates on the day. False (the default) gives back only their value: with no sale to point to, there is no tax charged to point to either."},"receiptless_return_permission":{"anyOf":[{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"]},{"type":"null"}],"description":"The permission a key must hold, at the location, to take goods back without a receipt. payment.refund by default. NULL lets anyone at the counter."},"cancellation_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of an order the business keeps, as a percent of its lines before tax, when an open order is cancelled before anything is handed over: the cancellation fee. Kept only out of money paid, never charged beyond it, and posted as fee income. 0 (the default) keeps nothing, so every payment is refunded before the order can be cancelled. A category's own cancellation_fee_percent overrides it for its items. To waive the fee, refund what was paid before cancelling. A decimal string with up to 2 decimal places, for example \"12.50\"."},"restocking_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of what goods returned on an order sold for, before tax, that the business keeps when they come back: the restocking fee, posted as fee income and kept from what is refunded (or owed, when the goods were not paid for). 0 (the default) keeps nothing. A category's own restocking_fee_percent overrides it for its items. Waiving it on a return needs order.discount where the goods come back. Goods taken back without a receipt are valued by their own rules and carry no fee. A decimal string with up to 2 decimal places, for example \"12.50\"."},"layaway_cancellation_fee_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The part of a layaway the business keeps, as a percent of its lines before tax, when it is cancelled (the customer stops paying, or calls it off) before the goods go home: the layaway's forfeit or restocking fee. It takes the place of the cancellation fees for an order on layaway. Kept only out of money paid, never charged beyond it, as any cancellation fee. 0 (the default) keeps nothing: every payment is refunded before the layaway can be cancelled. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fees_taxed":{"type":"boolean","description":"True when the restocking and cancellation fees the business keeps are taxed, as some states tax a restocking fee: each fee is taxed in the order's tax area at the rates in force on the day it is kept, for the class of the goods it was kept on, and the tax is owed to each jurisdiction like any other sales tax. A cancellation fee is then kept tax included, out of what was paid. An order that is tax-exempt or has no tax area pays no tax on its fees. False (the default) taxes no fee."},"quote_valid_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":365},{"type":"null"}],"description":"How many days a new quote's prices hold when it is written up with no expires_on: expires_on is set to its order date plus this. NULL (the default) leaves a quote with no expiry date unless one is given."},"quote_holds_units":{"type":"boolean","description":"True (the default) when a quote that names a serialized unit holds it for the customer until the quote expires, is opened or is cancelled, so no one else can buy it. False when only an open order holds a unit: a quote names the unit it offers, but anyone may sell it meanwhile, and opening the quote is refused if it has gone. Turning it back on is refused while two quotes name the same unit."},"refund_to_original_method":{"type":"boolean","description":"True when a refund on an order goes back only by the methods the order was paid with, each up to what was paid by it less what has gone back by it: the card back to the card, cash back as cash. Refunding by another method then needs ledger.post where it is paid out. False (the default) lets a refund go out by any method. Store credit always goes back to store credit first, whatever this says."},"min_deposit_percent":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The least a customer must have paid, as a percent of the order's total with tax, before a quote is opened into an order or an order is written up open: the deposit that protects a special order placed with the factory. openSalesOrder can take the deposit in the same step. 0 (the default) needs nothing. A key holding credit.manage where the sale is made may open an order with less. Counter sales are paid in full, and orders made by subscriptions, work orders and sales channels are not held to it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"below_cost_needs_approval":{"type":"boolean","description":"True when pricing a line so it sells for less than the item costs needs order.discount where the sale is made, whatever discount a role allows. The cost is what the goods would leave stock at there: what that location holds (its oldest layer under FIFO, else its average), else the cost the books assume for goods it does not hold (app.assumed_unit_cost: its last cost there, then other locations' stock, then the vendors'). False (the default) leaves only the discount rules."},"return_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":3650},{"type":"null"}],"description":"How many days after goods were handed over they are taken back on their order, with the receipt: 30 takes them back for 30 days. A category's own returnable_days overrides it for its items. A return past it needs order.cancel where the goods come back. NULL (the default) takes goods back at any time."},"receiptless_customer_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most one customer can be given back for goods without a receipt over receiptless_customer_days days, tax included, all their returns together. Set, every return without a receipt must name its customer. NULL (the default) sets no such limit; receiptless_return_limit still holds for each return."},"receiptless_customer_days":{"type":"integer","minimum":1,"maximum":730,"description":"The days, counting the day of the return, over which a customer's returns without a receipt are added up against receiptless_customer_limit. 30 by default."},"receiptless_cash_for_credit_purchases":{"type":"boolean","description":"Under receiptless_returns any_tender: true lets goods a named customer bought with store credit or a gift card come back without a receipt for money. False (the default) gives them back only as store credit: an item the customer bought on an order paid in part with store credit or a gift card, within receiptless_return_price_days (or ever, when that is NULL), comes back as store credit. Goods a lender paid for never come back without a receipt: they go back on their order, to the lender. Goods brought back by someone not named cannot be traced to a purchase."},"receiptless_traced_days":{"type":"integer","minimum":0,"maximum":730,"description":"How many days back a return without a receipt looks for sales of the item on orders a lender or a gift card paid for. Within them, the goods come back for money only when the customer named is the one who bought them that way (and then by the rules for that buyer); brought back by someone not named, or by another customer, they may be those goods, so they go back on the order they were sold on, or as store credit to a named customer. 90 by default; 0 turns the check off. Goods bought with a gift card are let through when receiptless_cash_for_credit_purchases is true; goods a lender paid for never are."},"package_discount_allocation":{"type":"string","enum":["proportional","main_component"],"description":"How a package's price is shared among its components, which decides each one's revenue, tax, commission and what a return of it gives back. proportional: in proportion to their regular prices, so each is discounted by the same share. main_component: the discount comes off the package's main component (the spa) first, as far as its own price goes, so accessories keep their regular prices; the rest, if any, in proportion. Applies when a package's price is next worked out."},"package_drop_credit":{"type":"string","enum":["none","share"],"description":"What the price of a package does when the customer leaves out a component the package lets go (package_component.can_drop). none: the package price holds, and the components kept share it. share: the package price comes down by what the component was priced at in the package."},"stock_aging_bucket_days":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"The ages, in days on hand, where the stock aging read starts a new bucket, in increasing order: the default 90, 180, 270 gives 0-90, 91-180, 181-270 and over 270."},"receivable_aging_bucket_days":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"The days overdue where the receivables aging starts a new bucket, in increasing order: the default 30, 60, 90 gives current (not yet past due), 1-30, 31-60, 61-90 and over 90 days overdue."},"site_readiness_checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The checklist a site check answers when none is named: what this business checks before a delivery (pad, electrical, access). NULL until it has one."},"delivery_checklist_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"A checklist the crew answer at every delivery before it is signed off. NULL asks nothing."},"delivery_sign_off_required":{"type":"boolean","description":"True: a delivery is completed only with the customer's signature, or, by a key holding delivery.override, as left with nobody there, with photos. False: completeAppointment may complete a delivery with no sign-off."},"account_customers_pay_on_site":{"type":"boolean","description":"True: a customer with payment terms is asked to pay at the door like anyone, and the crew's day and the schedule show what to collect from them. False (the default): what such a customer owes goes on their account and statement, and the field shows it as on account rather than to collect."},"delivery_acceptance_statement":{"type":"string","description":"What the customer signs under at a delivery. Copied onto each sign-off as it stood then."},"dispatch_requires_ready_site":{"type":"boolean","description":"True: a crew cannot be marked on its way to a delivery whose site is not shown ready, except by a key holding delivery.override, giving a reason. False: readiness is shown, never enforced."},"site_ready_days_before":{"type":"integer","minimum":0,"maximum":60,"description":"How many days before a delivery its site must be shown ready. From then a delivery not ready is overdue, and the customer is reminded what is still needed."},"appointment_reminder_hours":{"anyOf":[{"type":"integer","minimum":1,"maximum":720},{"type":"null"}],"description":"How many hours before a visit the customer is reminded of it (the appointment.upcoming event). NULL sends no reminders."},"redelivery_fee_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service item charged when a delivery fails for a reason in redelivery_fee_reasons, at its price. NULL charges nothing."},"redelivery_fee_reasons":{"maxItems":10,"type":"array","items":{"type":"string","enum":["site_not_ready","access_blocked","customer_absent","customer_refused","unit_damaged","wrong_unit","outside_party_no_show","weather","vehicle_or_crew","other"]},"description":"The reasons a failed delivery is charged for: any of site_not_ready, access_blocked, customer_absent, customer_refused, unit_damaged, wrong_unit, outside_party_no_show, weather, vehicle_or_crew, other. Empty charges for none."},"redelivery_party_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The service item a charged failure passes on what each outside party the business booked (a crane) and marked done bills for the visit as: one line of it per party, at their cost_amount, beside the redelivery fee and reported apart from it. NULL passes nothing on."},"service_labor_item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The item hourly labor on a job is charged as, unless the job names another: a service sold by the hour, in fractions. NULL: a job's customer time cannot be charged until one is set, and finishing a visit with some says so."},"travel_time_billing":{"type":"string","enum":["no_charge","customer"],"description":"Who pays for a technician's travel to a job, by default: no_charge (the default; most businesses charge a service-call fee instead, so travel is recorded for cost only) or customer (charged by the hour as labor)."},"labor_billing_increment_minutes":{"type":"integer","minimum":1,"maximum":60,"description":"Customer time on a visit is rounded up to this many minutes before it is charged. 1 (the default) charges the minutes worked."},"labor_billing_minimum_minutes":{"type":"integer","minimum":0,"maximum":480,"description":"The least time charged on a visit with any customer time. Time on a job but on no visit pays it only when none of the job's visits has customer time. 0 (the default) sets no minimum."},"service_sign_off_required":{"type":"boolean","description":"True (the default: the signature is the dealer's proof the customer accepted the work and its charges) when a service visit is completed only with the customer's signature on the completed work, or, by someone holding labor.manage, recorded as done with nobody there, with photos."},"service_authorization_statement":{"type":"string","minLength":1,"maxLength":2000,"description":"What a customer signs under when authorizing work on a job before it is done."},"service_completion_statement":{"type":"string","minLength":1,"maxLength":2000,"description":"What a customer signs under when accepting work on a job as done."},"default_labor_cost_rate":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What an hour of a technician's time costs the business when they have no cost rate of their own: wages plus burden. Reported only, never posted. NULL: their time has no cost in reports, and the reports say so."},"field_record_max_age_days":{"type":"integer","minimum":1,"maximum":90,"description":"How many days back a technician's device may date the time and parts it records on a job: what an offline device uploads late. Older needs labor.manage. 14 by default."},"service_labor_item_sku":{"description":"The labor item's SKU.","type":["string","null"]},"account_payment_application":{"type":"string","enum":["oldest_first","none"],"description":"What happens to a payment on account (takeAccountPayment) when the request does not say. oldest_first (the default): it pays the orders the customer owes on, the one due first first, and whatever is left stays as account credit. none: it all stays as account credit until someone applies it (applyAccountCredit)."},"cash_needs_drawer":{"type":"boolean","description":"True: cash taken or given back (a payment, a refund, a return without a receipt) must name an open drawer at the location it happens, so every dollar is in some drawer's count. False (the default): naming a drawer is optional, and cash not in a drawer posts to the cash method's account as before. A payment that turned out not to be money (a reversal) never needs one."},"cash_count_blind":{"type":"boolean","description":"True (the default): whoever counts a drawer is not shown what it should hold, nor how far over or short the count was, nor which drawer took each payment. Only a key holding cash.manage or cost.view at the location sees those figures: cost.view reads the books, which show what each drawer holds, so the business decides who counts blind by who holds cost.view. False: everyone who can see a drawer sees them."},"cash_count_by_denomination":{"type":"boolean","description":"True: closing or recounting a drawer needs the count by denomination (twenty $20 bills, ...), which must add up to the figure counted. False (the default): the total alone will do, and a count by denomination is kept when one is sent."},"check_number_required":{"type":"boolean","description":"True (the default): money taken by check, on an order, on account or for a gift card, needs the check's number in reference, so the deposit slip and the bank statement can be matched, and so does money given back by a check the business writes: a refund on an order, account credit, a gift card cashed out. False: the number may be left out."},"cash_over_short_tolerance":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How far over or short a drawer may be counted and still be reconciled when it is closed, with no manager looking at it. 0 (the default) sends every drawer that is off by a cent to a manager (reconcileCashDrawer, cash.manage)."},"cash_paid_out_limit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most cash that may be paid into or out of a drawer at once, for a reason other than a sale, without cash.manage at the location. 0 (the default) needs cash.manage for every paid-in and paid-out."},"cash_float_limit":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The largest float a drawer may be opened with, out of the safe, without cash.manage at the location. 0 (the default) needs cash.manage for any float; a drawer opened empty needs none."},"gift_cards_offered":{"type":"boolean","description":"True when the business sells gift cards: selling, reloading and giving promotional cards, and refunding money an order took onto a card as new value. False by default, so no card is sold until the business decides to. Cards already sold can always be spent, replaced and written off, and the card value an order holds refunded onto a card (a new one too), whatever this says."},"gift_card_max_load":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The most that can be put onto one gift card in one business day, sold, reloaded and given together. 2000.00 by default: the federal threshold under which a store's own cards are not treated as prepaid access a money services business must register for. Changing it is the business's call, and its accountant's."},"gift_card_refund_to_card":{"type":"boolean","description":"True (the default) when money an order was paid by gift card goes back onto a gift card before anything is refunded as money (or into account credit, which is money), so a card is never turned into cash by a return. False lets it go back by any method."},"gift_card_cash_out_below":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"A card holding less than this can be cashed out on request (cashOutGiftCard), as some states require (California: under 10.00). NULL (the default) cashes out no card."},{"type":"null"}],"description":"A card holding less than this can be cashed out on request (cashOutGiftCard), as some states require (California: under 10.00). NULL (the default) cashes out no card."},"gift_card_breakage_after_days":{"anyOf":[{"type":"integer","minimum":1,"maximum":36500},{"type":"null"}],"description":"How many days a card must go unused before what it holds may be written off as breakage, taken into income as never to be spent (writeOffGiftCard). NULL (the default) takes no breakage: the balance stays owed until it is spent or written off as unclaimed property. Whether and when breakage may be taken is a question for the business's accountant."},"gift_card_escheat_after_days":{"type":"integer","minimum":1,"maximum":36500,"description":"How many days a card must go unused before what it holds may be written off as unclaimed property owed to the state (writeOffGiftCard, escheat). 1825 (five years) by default, the longest dormancy period any state uses, so no card is reported early; set it to the business's state's period. Idaho exempts cards that neither expire nor charge fees, and these do neither, so a business there may never need it."},"payment_link_valid_days":{"type":"integer","minimum":1,"maximum":60,"description":"How many days a payment link sent to a customer works for (sendPaymentLink), counted from when it is sent. 7 by default. Once it has passed the customer is sent a new one."},"receipt_header":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"The business's own words printed at the top of every receipt, under its name and address, for example a slogan or \"Thank you for shopping local\". Up to 500 characters. NULL (the default): nothing is printed there."},{"type":"null"}],"description":"The business's own words printed at the top of every receipt, under its name and address, for example a slogan or \"Thank you for shopping local\". Up to 500 characters. NULL (the default): nothing is printed there."},"receipt_footer":{"anyOf":[{"type":"string","minLength":1,"maxLength":500,"description":"The business's own words printed at the bottom of every receipt, for example its website or service hours. Up to 500 characters. NULL (the default): nothing is printed there."},{"type":"null"}],"description":"The business's own words printed at the bottom of every receipt, for example its website or service hours. Up to 500 characters. NULL (the default): nothing is printed there."},"receipt_return_policy":{"anyOf":[{"type":"string","minLength":1,"maxLength":1000,"description":"The business's return policy as it wants customers to read it, printed on every receipt and gift receipt, for example \"Unopened chemicals may be returned within 30 days with this receipt.\" Up to 1000 characters. NULL (the default): no return policy is printed."},{"type":"null"}],"description":"The business's return policy as it wants customers to read it, printed on every receipt and gift receipt, for example \"Unopened chemicals may be returned within 30 days with this receipt.\" Up to 1000 characters. NULL (the default): no return policy is printed."},"receipt_email_automatically":{"type":"boolean","description":"True: each receipt issued for a customer with an email address is emailed to them as it is issued, as a service message their consent allows. False (the default): a receipt is emailed only when someone asks (emailReceipt)."},"receipt_shows_regular_price":{"type":"boolean","description":"True (the default): a receipt line sold below what it lists at for the sale, at a price set by hand, prints that price as its regular price beneath it, so the customer sees what they saved. False: receipts print only the price agreed."},"fiscal_year_start_month":{"type":"integer","minimum":1,"maximum":12,"description":"The month the business's fiscal year starts, 1 for January to 12 for December. Reports count fiscal quarters and years from it, and the financial statement counts earnings for the year from it. 1 (the default): the fiscal year is the calendar year."},"week_start_day":{"type":"string","enum":["monday","sunday"],"description":"The day a week starts on in reports grouped by week: monday (the default, as ISO weeks) or sunday."},"staff_session_hours":{"type":"integer","minimum":1,"maximum":24,"description":"How long a person stays signed in, in hours from 1 to 24: a sign-in issues a session key that stops working this long after, on the business's clock, and they sign in again. 12 (the default) covers a working day. A change applies to sign-ins from then on."},"shared_device_idle_minutes":{"type":"integer","minimum":1,"maximum":120,"description":"On a computer a person marked as shared when signing in (a store counter), how many minutes from 1 to 120 without anyone using it before the person is signed out, so the next person cannot act as them. 5 (the default) suits a busy counter. The web app applies it; a session key itself still lasts staff_session_hours."},"pin_length":{"type":"integer","minimum":4,"maximum":8,"description":"How many digits a new PIN has, from 4 to 8: what a person sets to switch in on a counter device (setPin). 6 (the default) gives a million to guess from, and five wrong ones lock it. A PIN set before a change keeps working."},"device_session_minutes":{"type":"integer","minimum":5,"maximum":720,"description":"How long a person switched in on a counter device with their PIN stays signed in there, in minutes from 5 to 720: the session stops working this long after, on the business's clock, and never after the device itself. 30 (the default) covers a busy stretch at the counter."},"device_idle_minutes":{"type":"integer","minimum":1,"maximum":60,"description":"On a counter device, how many minutes from 1 to 60 without anyone using it before the person switched in is signed out and the PIN pad shows again. 5 (the default) suits a busy counter. The web app applies it."},"device_sign_out_after_sale":{"type":"boolean","description":"True (the default) when a person switched in on a counter device is signed out once a sale is done with, so the next person switches in as themselves and the sale is credited right. False keeps them signed in until they sign out or the device sits idle. The web app applies it."},"sales_desk_idle_minutes":{"type":"integer","minimum":1,"maximum":120,"description":"While a quote or order is open on screen, how many minutes from 1 to 120 without anyone using it before a person on a shared computer or counter device is signed out, when that is longer than the usual idle limit (shared_device_idle_minutes, device_idle_minutes). A customer reads a quote across the desk for minutes without a tap; 15 (the default) lets them, and anything else on screen keeps the usual limit. The web app applies it."},"lead_owner":{"type":"string","enum":["captor","unassigned"],"description":"Who owns a lead someone takes down without naming an owner: captor (the default), the person who took it down, as a showroom works; unassigned, every new lead waits for a manager (lead.manage) to hand it out. A lead from a form or another system, taken down by no one, always waits."},"lead_follow_up_days":{"anyOf":[{"type":"integer","minimum":0,"maximum":60},{"type":"null"}],"description":"When the first follow-up on a new lead falls due, in days after it is taken down, unless the person taking it down sets one: 1 (the default) is the next day, 0 the same day. NULL: no follow-up is set unless one is given."},"lead_stale_days":{"type":"integer","minimum":1,"maximum":365,"description":"How many days an open lead may go with nothing logged before it shows as going cold. 14 by default."},"site_readiness_checklist_name":{"description":"That checklist's name.","type":["string","null"]},"delivery_checklist_name":{"description":"That checklist's name.","type":["string","null"]},"redelivery_fee_item_sku":{"description":"The fee item's SKU.","type":["string","null"]},"redelivery_fee_item_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The fee item's price: what a failed delivery is charged."},"redelivery_party_item_sku":{"description":"The SKU outside parties' bills are passed on as, or null when they are not passed on.","type":["string","null"]},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["inventory_costing_method","allow_negative_stock","unknown_lot_allowed","stock_count_blind","stock_count_missing_units","par_level_replenishment","books_closed_through","new_customer_credit_limit","tax_rounding","tax_rate_date","carry_out_elsewhere","exemption_without_certificate","new_customer_payment_terms_days","hold_when_overdue_days","finance_charges","finance_charge_monthly_percent","finance_charge_minimum","finance_charge_min_overdue","finance_charge_grace_days","finance_charge_compounds","store_credit_offered","receiptless_returns","receiptless_return_price_days","receiptless_return_limit","receiptless_return_refunds_tax","receiptless_return_permission","cancellation_fee_percent","restocking_fee_percent","layaway_cancellation_fee_percent","fees_taxed","quote_valid_days","quote_holds_units","refund_to_original_method","min_deposit_percent","below_cost_needs_approval","return_days","receiptless_customer_limit","receiptless_customer_days","receiptless_cash_for_credit_purchases","receiptless_traced_days","package_discount_allocation","package_drop_credit","stock_aging_bucket_days","receivable_aging_bucket_days","site_readiness_checklist_id","delivery_checklist_id","delivery_sign_off_required","account_customers_pay_on_site","delivery_acceptance_statement","dispatch_requires_ready_site","site_ready_days_before","appointment_reminder_hours","redelivery_fee_item_id","redelivery_fee_reasons","redelivery_party_item_id","service_labor_item_id","travel_time_billing","labor_billing_increment_minutes","labor_billing_minimum_minutes","service_sign_off_required","service_authorization_statement","service_completion_statement","default_labor_cost_rate","field_record_max_age_days","service_labor_item_sku","account_payment_application","cash_needs_drawer","cash_count_blind","cash_count_by_denomination","check_number_required","cash_over_short_tolerance","cash_paid_out_limit","cash_float_limit","gift_cards_offered","gift_card_max_load","gift_card_refund_to_card","gift_card_cash_out_below","gift_card_breakage_after_days","gift_card_escheat_after_days","payment_link_valid_days","receipt_header","receipt_footer","receipt_return_policy","receipt_email_automatically","receipt_shows_regular_price","fiscal_year_start_month","week_start_day","staff_session_hours","shared_device_idle_minutes","pin_length","device_session_minutes","device_idle_minutes","device_sign_out_after_sale","sales_desk_idle_minutes","lead_owner","lead_follow_up_days","lead_stale_days","site_readiness_checklist_name","delivery_checklist_name","redelivery_fee_item_sku","redelivery_fee_item_price","redelivery_party_item_sku","updated_at"],"additionalProperties":false,"description":"The business rules a tenant chooses for itself, one row per tenant, created with the tenant. Each rule is a typed column so the database can enforce it."},"CustomField":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"table_name":{"type":"string","enum":["appointment","customer","equipment","item","purchase_order","sales_order","serialized_unit","site","subscription","vendor","work_order"],"description":"The table the field extends, for example item or customer. Only tables with a custom_fields column can be extended. Fixed once the field exists."},"key":{"type":"string","description":"The field's name inside custom_fields: lowercase snake_case, unique per table. Fixed once the field exists."},"name":{"type":"string","description":"Label shown to people, for example \"Seats\"."},"description":{"description":"What the field means and when to fill it in, written for someone (or a model) who has never seen the business.","type":["string","null"]},"data_type":{"type":"string","enum":["text","number","boolean","date","choice"],"description":"text, number, boolean, date (a YYYY-MM-DD string) or choice (one of choices). Fixed once the field exists."},"choices":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The allowed values when data_type is choice. NULL otherwise."},"is_active":{"type":"boolean","description":"False once the field is retired: values already stored stay, but it can no longer be set."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","table_name","key","name","description","data_type","choices","is_active","created_at","updated_at"],"additionalProperties":false,"description":"The definition of one field a tenant has added to one of the shared tables, for example \"seats\" on item for a hot tub dealer. Values live in that table's custom_fields column as {key: value} and are checked against these definitions on every write."},"CustomFieldPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomField"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of custom fields."},"CustomFieldValues":{"type":"object","properties":{"table_name":{"type":"string","description":"The table of the record."},"row_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The id of the record."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"All of the record's custom field values, as {key: value}."}},"required":["table_name","row_id","custom_fields"],"additionalProperties":false,"description":"A record's custom field values."},"DayCloseTender":{"type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The payment method."},"payment_method_name":{"type":"string","description":"The method's name as staff see it, unique within the tenant."},"kind":{"type":"string","enum":["cash","check","card","financing","other","store_credit","account_credit","gift_card","trade_in"],"description":"What sort of method it is: cash, check, card, financing (a lender pays the tenant), other, store_credit, account_credit or gift_card. store_credit is the one method the system makes for paying with a customer's store credit and refunding into it: it posts to the Store Credit account and moves the customer's balance. account_credit (\"On account\") is the one the system makes for paying an order out of money the customer paid on account, and for moving money held on an order back into their account credit: it posts to Customer Payments on Account. gift_card is the one the system makes for paying with a gift card and refunding onto one, naming the card: it posts to Gift Cards Outstanding and moves the card's balance. trade_in (\"Trade-in\") is the one the system makes for what a customer is allowed for an old unit they trade in toward an order: it posts to Trade-Ins Clearing, and only receiving or giving back the trade (receiveTradeIn, giveBackTradeIn) pays or refunds by it. None of them can be added, and no other method can become one."},"received":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Taken in: order payments, payments on account and gift cards sold or reloaded. A decimal string with up to 2 decimal places, for example \"12.50\"."},"given_back":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Paid out of what was taken, as a positive amount: refunds, account credit given back, gift card sales voided and cards cashed out, and returns without a receipt. For a check method, only a gift card sale voided, its check handed back: a check the business writes is in written_on_bank. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reversed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Payments that turned out not to be money (a bounced check, a chargeback, a customer's check handed back uncashed), taken back, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"written_on_bank":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"For a check method: checks the business wrote to give money back (refunds, account credit, a gift card cashed out, a return without a receipt), as a positive amount. They are drawn on its bank account and never come out of a deposit, so they are not in net. 0.00 for any other method. A decimal string with up to 2 decimal places, for example \"12.50\"."},"net":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"received less given_back less reversed: what the drawers, the deposit slip or the card batch hold for it. For checks it is what the day's checks still on the slip add up to, banked or not. A decimal string with up to 2 decimal places, for example \"12.50\"."},"entries":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many payments, refunds and other entries make it up."}},"required":["payment_method_id","payment_method_name","kind","received","given_back","reversed","written_on_bank","net","entries"],"additionalProperties":false,"description":"One way customers paid at a store on a day, and how much."},"DayCloseDrawer":{"type":"object","properties":{"cash_drawer_session_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The drawer session."},"cash_drawer_session_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote for the drawer, unique within the business and assigned automatically."},"register_name":{"type":"string","description":"What staff call the register, for example \"Front counter\"."},"register_code":{"type":"string","description":"A short code for the register, unique at its location: up to 8 capital letters and digits, for example FRONT or R2."},"status":{"type":"string","enum":["open","closed","reconciled"],"description":"open: taking cash. closed: counted, and over or short by more than the business allows, waiting for a manager to reconcile it. reconciled: counted and settled, within the tolerance at once, or by a manager."},"opened_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who opened the drawer and counted the float in."},"opened_by_staff_member_name":{"description":"Their name, or null when none is recorded.","type":["string","null"]},"opened_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date the drawer was opened, at the location. Calculated."},"closed_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the drawer was closed, at the location. NULL while it is open. Calculated."},"opening_float":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The cash put in the drawer to make change, taken out of the location's safe. 0 for a drawer opened empty. A decimal string with up to 2 decimal places, for example \"12.50\"."},"expected_cash":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the drawer should hold by its records: the float, plus cash taken and paid in, less cash given back, paid out and dropped. Kept as the drawer's entries are written; fixed at what it was once the drawer is closed. Hidden from whoever counts when the business counts blind. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"counted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The cash counted in the drawer when it was closed, all of which goes into the safe. Never changed afterwards: a recount is recounted_cash."},"recounted_cash":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What a manager counted when reconciling, if the cash was counted again. A figure other than counted_cash moves the difference into or out of the safe, against Cash Over and Short. NULL when it was not recounted."},"over_short":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the drawer was over (positive) or short (negative) in the end: the recount when there was one, else the count, less expected_cash. Null while open."},"needs_reconcile":{"type":"boolean","description":"True when it is closed over or short past the tolerance, waiting for a manager."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["cash_drawer_session_id","cash_drawer_session_number","register_name","register_code","status","opened_by_staff_member_id","opened_by_staff_member_name","opened_on","closed_on","opening_float","expected_cash","counted_cash","recounted_cash","over_short","needs_reconcile","next"],"additionalProperties":false,"description":"A drawer at the store that was open on the day, or is still waiting to be settled."},"DayCloseSafe":{"type":"object","properties":{"opening_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the safe held at the start of the day, by its records. A decimal string with up to 2 decimal places, for example \"12.50\"."},"funded":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash put in from outside the drawers. A decimal string with up to 2 decimal places, for example \"12.50\"."},"floats_out":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Opening floats taken out to drawers, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"drops_in":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash drawers dropped into it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closes_in":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Closed drawers' counts put into it. A decimal string with up to 2 decimal places, for example \"12.50\"."},"adjustments":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Recounts of closed drawers, counts of the safe and cash reversals: what they moved, signed. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposited":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Cash taken to the bank, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closing_balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the safe held at the end of the day, by its records. A decimal string with up to 2 decimal places, for example \"12.50\"."},"deposits":{"type":"array","items":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"How much was taken to the bank. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"For a deposit, the deposit slip or bag number.","type":["string","null"]},"staff_member_name":{"description":"Who took it, when recorded.","type":["string","null"]}},"required":["amount","reference","staff_member_name"],"additionalProperties":false},"description":"Each trip to the bank on the day, in the order made."}},"required":["opening_balance","funded","floats_out","drops_in","closes_in","adjustments","deposited","closing_balance","deposits"],"additionalProperties":false,"description":"The store's safe over the day."},"DayCloseException":{"type":"object","properties":{"kind":{"type":"string","enum":["drawer_open","drawer_not_reconciled","cash_without_drawer","cash_given_back_without_drawer"],"description":"drawer_open: a drawer is still taking cash. drawer_not_reconciled: a drawer was closed over or short past the tolerance and waits for a manager. cash_without_drawer: cash was taken without naming a drawer, so no count covers it. cash_given_back_without_drawer: cash was given back (a refund, a card cashed out) without naming a drawer, so no count shows it went out."},"detail":{"type":"string","description":"What is wrong, with the figures."},"cash_drawer_session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The drawer it is about, if any."},"cash_drawer_session_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"That drawer's number."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["kind","detail","cash_drawer_session_id","cash_drawer_session_number","next"],"additionalProperties":false,"description":"Something to settle before the day is closed."},"DayCloseLocation":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"branch_code":{"type":"string","description":"Short uppercase code staff use to name the location, unique within the tenant, for example BOI."},"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business day, at the store."},"is_final":{"type":"boolean","description":"True when the books are closed through the day: nothing more can be posted on it."},"sales":{"anyOf":[{"type":"object","properties":{"delivered":{"$ref":"#/components/schemas/DeliveredSales"},"booked":{"$ref":"#/components/schemas/BookedSales"}},"required":["delivered","booked"],"additionalProperties":false},{"type":"null"}],"description":"The store's sales that day, as getSalesReport counts them. Null for a key without sales.view at the store."},"tenders":{"type":"array","items":{"$ref":"#/components/schemas/DayCloseTender"},"description":"Money that changed hands at the counter that day (cash, check, card, other), by payment method and name: what the drawers, the deposit slip and the card batch hold. Their nets, less what written_on_bank paid out, add up to money_for.total."},"money_for":{"type":"object","properties":{"orders":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Paid on orders, less refunds and reversals: what went to deposits and receivables. A decimal string with up to 2 decimal places, for example \"12.50\"."},"on_account":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Paid on account (takeAccountPayment), less refunds and reversals. A decimal string with up to 2 decimal places, for example \"12.50\"."},"gift_cards":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Gift cards sold and reloaded, less sales voided and cards cashed out. A decimal string with up to 2 decimal places, for example \"12.50\"."},"returns_without_receipt":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Given back for goods returned without a receipt, as a negative amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What tenders took in all, net: the sum of the lines above. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["orders","on_account","gift_cards","returns_without_receipt","total"],"additionalProperties":false,"description":"What the money in tenders was for, so it reconciles: a gift card sold for cash or a check paid on account is money in the drawer with no sale behind it."},"value_moved":{"type":"array","items":{"$ref":"#/components/schemas/DayCloseTender"},"description":"Tenders that moved value rather than took money, apart from tenders: store credit, account credit (On account, as when a payment on account is applied to orders, whose money is the payment's own tender), a gift card spent or refunded onto, and financing a lender pays later. Not in the drawers or on the deposit slip."},"deposits":{"type":"object","properties":{"taken":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Order payments that day that went to orders with goods still to hand over (Customer Deposits), net of deposits refunded. A decimal string with up to 2 decimal places, for example \"12.50\"."},"taken_for_later":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Of taken, what was paid on orders that handed nothing over that day: deposits on goods still to come, leaving out a counter sale's payment, which is taken and used at once. A decimal string with up to 2 decimal places, for example \"12.50\"."},"used":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Deposits the store's hand-overs that day used up, paying for the goods handed over: a counter sale's payment is taken and used the same day. A decimal string with up to 2 decimal places, for example \"12.50\"."},"net_change":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"taken less used: how much more the store holds on deposit than it did that morning. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_to_receivables":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Order payments that day that paid for goods already handed over (Accounts Receivable), net. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["taken","taken_for_later","used","net_change","paid_to_receivables"],"additionalProperties":false,"description":"Money held for customers' undelivered goods, against money for goods they already have, as the books carry it."},"checks":{"type":"array","items":{"$ref":"#/components/schemas/Check"},"description":"Every check taken that day, in the order taken, each with the bank deposit that carried it, as the day stood: a check reversed or voided by the end of it (it bounced, or was handed back to the customer uncashed) is left out, and what was taken back of one comes off its amount. One reversed on a later day stays, and that day lists it in checks_reversed."},"checks_reversed":{"type":"array","items":{"$ref":"#/components/schemas/CheckReversal"},"description":"Checks reversed or voided this day, in the order reversed: bounced, or handed back uncashed. One taken this day is already off its amount in checks; one taken on an earlier day is off this day's check net, not that day's."},"checks_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the checks add up to, less the checks_reversed taken on an earlier day: the check method's net. A decimal string with up to 2 decimal places, for example \"12.50\"."},"checks_to_bank":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the day's checks not yet banked add up to: what is still to go to the bank. A decimal string with up to 2 decimal places, for example \"12.50\"."},"checks_written":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["payment","account_payment","gift_card","receiptless_return"],"description":"What the check paid back: payment, a refund on an order; account_payment, account credit given back; gift_card, a card cashed out; receiptless_return, goods returned without a receipt."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order refunded, for a refund on an order."},"gift_card_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The card cashed out, for a gift card."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who it was written to, when known."},"customer_name":{"description":"Their name.","type":["string","null"]},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What it was written for, as a positive amount. A decimal string with up to 2 decimal places, for example \"12.50\"."},"reference":{"description":"The number of the check written.","type":["string","null"]}},"required":["source","order_number","gift_card_number","customer_id","customer_name","amount","reference"],"additionalProperties":false},"description":"Checks the business wrote at the store that day to give money back, in the order written: drawn on its bank account, never out of the deposit."},"drawers":{"type":"array","items":{"$ref":"#/components/schemas/DayCloseDrawer"},"description":"Drawers open that day or still waiting to be settled, by number."},"over_short":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the day's drawers were over or short in all, counted ones only. A decimal string with up to 2 decimal places, for example \"12.50\"."},"safe":{"anyOf":[{"$ref":"#/components/schemas/DayCloseSafe"},{"type":"null"}],"description":"The safe. Null when the store has none yet."},"exceptions":{"type":"array","items":{"$ref":"#/components/schemas/DayCloseException"},"description":"What still needs doing. Empty when the day is accounted for."}},"required":["location_id","location_name","branch_code","date","is_final","sales","tenders","money_for","value_moved","deposits","checks","checks_reversed","checks_total","checks_to_bank","checks_written","drawers","over_short","safe","exceptions"],"additionalProperties":false,"description":"One store's day: sales, money by method, checks, drawers and safe."},"DayClose":{"type":"object","properties":{"locations":{"type":"array","items":{"$ref":"#/components/schemas/DayCloseLocation"},"description":"One per store asked for, by branch code."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["locations","next"],"additionalProperties":false,"description":"Whether each store's money for a day is accounted for."},"StatementColumn":{"type":"object","properties":{"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location, or null for amounts posted to no location."},"location_name":{"type":"string","description":"The location's name, or 'No location' for amounts posted to none (most manual entries)."},"branch_code":{"description":"Its branch code. Null for no location.","type":["string","null"]},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The amount at that location. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["location_id","location_name","branch_code","amount"],"additionalProperties":false,"description":"One location's part of an amount."},"StatementAmounts":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"For the period, or at the date: positive on the section's usual side (income and liabilities are credits, costs and assets debits). A decimal string with up to 2 decimal places, for example \"12.50\"."},"prior":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The same in the compared period. Null when compare is none."},"change":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"amount less prior. Null when compare is none."},"change_percent":{"description":"change as a percent of prior, to one decimal place. Null when compare is none or prior is 0.","type":["string","null"]},"by_location":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/StatementColumn"}},{"type":"null"}],"description":"The amount split by location, one entry per column of the statement. Null unless by_location."}},"required":["amount","prior","change","change_percent","by_location"],"additionalProperties":false,"description":"A figure on a statement, with its comparison and its split by location when asked for."},"StatementLine":{"type":"object","properties":{"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The account."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"For the period, or at the date: positive on the section's usual side (income and liabilities are credits, costs and assets debits). A decimal string with up to 2 decimal places, for example \"12.50\"."},"prior":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The same in the compared period. Null when compare is none."},"change":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"amount less prior. Null when compare is none."},"change_percent":{"description":"change as a percent of prior, to one decimal place. Null when compare is none or prior is 0.","type":["string","null"]},"by_location":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/StatementColumn"}},{"type":"null"}],"description":"The amount split by location, one entry per column of the statement. Null unless by_location."}},"required":["ledger_account_id","ledger_account_code","ledger_account_name","amount","prior","change","change_percent","by_location"],"additionalProperties":false,"description":"One account on a statement."},"StatementSection":{"type":"object","properties":{"kind":{"type":"string","enum":["income","cost_of_goods_sold","expense","asset","liability","equity"],"description":"What the account measures. asset, cost_of_goods_sold and expense accounts grow with debits; liability, equity and income accounts grow with credits. Income less cost_of_goods_sold is gross profit. Fixed once the account exists."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/StatementLine"},"description":"Each account, by code."},"total":{"description":"The section's accounts added up.","$ref":"#/components/schemas/StatementAmounts"}},"required":["kind","lines","total"],"additionalProperties":false,"description":"The accounts of one kind, and their total."},"TrialBalanceLine":{"type":"object","properties":{"ledger_account_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The account. Null for earnings of earlier fiscal years, worked out rather than posted."},"ledger_account_code":{"description":"Its code. Null for earnings of earlier fiscal years.","type":["string","null"]},"ledger_account_name":{"type":"string","description":"Its name."},"kind":{"type":"string","enum":["income","cost_of_goods_sold","expense","asset","liability","equity"],"description":"What the account measures. asset, cost_of_goods_sold and expense accounts grow with debits; liability, equity and income accounts grow with credits. Income less cost_of_goods_sold is gross profit. Fixed once the account exists."},"opening":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Debits less credits before from. Income and expense accounts count from the start of the fiscal year from falls in. A decimal string with up to 2 decimal places, for example \"12.50\"."},"debits":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Debits posted from from to to. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credits":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Credits posted from from to to. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closing":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"opening plus debits less credits: debits less credits at to. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["ledger_account_id","ledger_account_code","ledger_account_name","kind","opening","debits","credits","closing"],"additionalProperties":false,"description":"One account's opening balance, what was posted to it, and its closing balance."},"FinancialStatement":{"type":"object","properties":{"statement":{"type":"string","enum":["profit_and_loss","balance_sheet","trial_balance"],"description":"Which statement this is."},"from":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The first day counted. Null for a balance sheet, which is at a date."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day counted, or the date of a balance sheet."},"fiscal_year_start":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the fiscal year `to` falls in (getPolicy, fiscal_year_start_month)."},"compare":{"type":"string","enum":["none","prior_period","prior_week","prior_year","prior_year_weekday"],"description":"What the figures are set against."},"prior_from":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The compared period's first day. Null when compare is none or for a balance sheet."},"prior_to":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The compared period's last day, or the compared balance sheet's date. Null when compare is none."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location the statement is limited to, or null for the whole business."},"location_name":{"description":"Its name, or null for the whole business.","type":["string","null"]},"sections":{"type":"array","items":{"$ref":"#/components/schemas/StatementSection"},"description":"For a profit and loss: income, cost_of_goods_sold, expense. For a balance sheet: asset, liability, equity. Empty for a trial balance."},"gross_profit":{"anyOf":[{"$ref":"#/components/schemas/StatementAmounts"},{"type":"null"}],"description":"For a profit and loss: income less cost of goods sold. Null otherwise."},"gross_margin_percent":{"description":"For a profit and loss: gross profit as a percent of income, to one decimal place. Null otherwise, or when income is 0.","type":["string","null"]},"net_posted":{"anyOf":[{"$ref":"#/components/schemas/StatementAmounts"},{"type":"null"}],"description":"For a profit and loss: gross profit less the expenses posted here. Not net income: payroll, rent and other bills are in the accounting system (unposted_note). Null otherwise."},"retained_earnings":{"anyOf":[{"$ref":"#/components/schemas/StatementAmounts"},{"type":"null"}],"description":"For a balance sheet: what income less costs came to in fiscal years before the one `to` falls in, worked out from the journal. Null otherwise."},"current_year_earnings":{"anyOf":[{"$ref":"#/components/schemas/StatementAmounts"},{"type":"null"}],"description":"For a balance sheet: income less costs from fiscal_year_start to `to`. Null otherwise."},"liabilities_and_equity":{"anyOf":[{"$ref":"#/components/schemas/StatementAmounts"},{"type":"null"}],"description":"For a balance sheet: liabilities, equity and both earnings added up, which equal the assets. Null otherwise."},"is_balanced":{"description":"For a balance sheet, whether assets equal liabilities_and_equity; for a trial balance, whether debits equal credits. Always true while the books are consistent (getBooksCheck). Null for a profit and loss.","type":["boolean","null"]},"trial_balance":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/TrialBalanceLine"}},{"type":"null"}],"description":"For a trial balance: every account with a balance or postings, by code, then earnings of earlier fiscal years. Null otherwise."},"trial_balance_totals":{"anyOf":[{"type":"object","properties":{"opening":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Openings added up: 0. A decimal string with up to 2 decimal places, for example \"12.50\"."},"debits":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Every debit posted in the period. A decimal string with up to 2 decimal places, for example \"12.50\"."},"credits":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Every credit posted in the period: equal to debits. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closing":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Closings added up: 0. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["opening","debits","credits","closing"],"additionalProperties":false},{"type":"null"}],"description":"For a trial balance, its columns added up. Null otherwise."},"books_closed_through":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last date of the closed accounting period. Nothing can be posted on or before it, so figures already reported or exported cannot change. NULL when no period is closed."},"is_final":{"type":"boolean","description":"True when the books are closed through `to`, so nothing more can be posted that would change it."},"unposted_note":{"type":"string","description":"What these books leave out, which the accounting system holds."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["statement","from","to","fiscal_year_start","compare","prior_from","prior_to","location_id","location_name","sections","gross_profit","gross_margin_percent","net_posted","retained_earnings","current_year_earnings","liabilities_and_equity","is_balanced","trial_balance","trial_balance_totals","books_closed_through","is_final","unposted_note","next"],"additionalProperties":false,"description":"A profit and loss, a balance sheet or a trial balance of the accounts Retavon keeps, read live from the journal."},"AgingBucket":{"type":"object","properties":{"label":{"type":"string","description":"What the bucket holds: current (not yet past due), a range of days overdue such as 31-60, or 91+."},"from_days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The fewest days overdue in the bucket: 0 for current."},"to_days_overdue":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The most days overdue in the bucket. Null for the last, which has no limit."}},"required":["label","from_days_overdue","to_days_overdue"],"additionalProperties":false,"description":"One column of the aging: a range of days past due."},"AgingRow":{"type":"object","properties":{"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The customer, when grouped by customer. Null otherwise."},"customer_name":{"description":"Their name. Null unless grouped by customer.","type":["string","null"]},"customer_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Their customer number. Null unless grouped by customer."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location, when grouped by location. Null otherwise."},"location_name":{"description":"Its name. Null unless grouped by location.","type":["string","null"]},"branch_code":{"description":"Its branch code. Null unless grouped by location.","type":["string","null"]},"debts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many orders and parts owed on account the row adds up."},"amounts":{"type":"array","items":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},"description":"What is owed in each bucket, in the order of buckets: current first, then by days overdue."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Everything owed in the row: the buckets added up. A decimal string with up to 2 decimal places, for example \"12.50\"."},"overdue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of total that is past due: every bucket but current. A decimal string with up to 2 decimal places, for example \"12.50\"."},"oldest_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When the oldest debt in the row fell due."},"most_days_overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days past due the row's most overdue debt is: 0 when none is past due."},"credit_limit":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The most this customer may owe at one time, across all their orders. Goods are not handed over past it. NULL sets no limit; 0 means everything must be paid for before it is handed over. Null when grouped otherwise than by customer, or when they have none."},"on_hold":{"description":"Whether goods on account are held from them now: the business holds overdue accounts (getPolicy, hold_when_overdue_days), something they owe is later than that, and no release covers today. Null unless grouped by customer.","type":["boolean","null"]},"deposits_held":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they have paid on orders not yet handed over, at every location. Null unless grouped by customer."},"account_credit":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Money they paid on account not yet applied, which can pay what they owe (applyAccountCredit). Null unless grouped by customer."},"store_credit":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Store credit they hold, which can pay for goods. Null unless grouped by customer."}},"required":["customer_id","customer_name","customer_number","location_id","location_name","branch_code","debts","amounts","total","overdue","oldest_due_on","most_days_overdue","credit_limit","on_hold","deposits_held","account_credit","store_credit"],"additionalProperties":false,"description":"What one customer or location owes, by how late it is."},"ReceivablesAging":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today where the business is. Days overdue are counted at each debt's location."},"group_by":{"type":"string","enum":["customer","location","none"],"description":"What each row is."},"bucket_days":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"The days overdue where the receivables aging starts a new bucket, in increasing order: the default 30, 60, 90 gives current (not yet past due), 1-30, 31-60, 61-90 and over 90 days overdue."},"buckets":{"type":"array","items":{"$ref":"#/components/schemas/AgingBucket"},"description":"The buckets every row's amounts are in, current first."},"rows":{"type":"array","items":{"$ref":"#/components/schemas/AgingRow"},"description":"One per customer or location owing something, most owed first, up to limit."},"other":{"anyOf":[{"$ref":"#/components/schemas/AgingRow"},{"type":"null"}],"description":"Everyone past limit, added up into one row. Null when every row is shown."},"totals":{"description":"Everything shown and in other, added up.","$ref":"#/components/schemas/AgingRow"},"accounts_receivable":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The balance of the Accounts Receivable account in the journal, which totals.total equals. Null when the report is filtered, or the key does not see sales everywhere."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["as_of","group_by","bucket_days","buckets","rows","other","totals","accounts_receivable","next"],"additionalProperties":false,"description":"What customers owe, by how many days past due, in the business's buckets."},"WorkOrderAgeBucket":{"type":"object","properties":{"label":{"type":"string","description":"The ages in the bucket, in days open, for example 8-14, or 61+ for the last."},"paid":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open work orders the customer pays for."},"warranty":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open work orders under warranty."}},"required":["label","paid","warranty"],"additionalProperties":false,"description":"Open work orders of one age range."},"OldestWorkOrder":{"type":"object","properties":{"work_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The work order."},"work_order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store it belongs to."},"location_name":{"type":"string","description":"Its name."},"is_warranty":{"type":"boolean","description":"True when the work is covered by a warranty, so the customer is not charged for the covered part. Setting or clearing it needs warranty.manage at the job's location, and it is fixed once parts not billed to the customer, or time billed to warranty, are recorded on the job."},"problem":{"type":"string","description":"What the customer reported or asked for, in their terms."},"opened_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it was opened, at its store."},"days_open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days since then."},"next_visit_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The next visit booked for it. Null when none is booked."}},"required":["work_order_id","work_order_number","customer_id","customer_name","location_id","location_name","is_warranty","problem","opened_on","days_open","next_visit_on"],"additionalProperties":false,"description":"An open work order, among the longest open."},"WorkloadDay":{"type":"object","properties":{"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day, at the store."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store the visits are booked from."},"location_name":{"type":"string","description":"Its name."},"branch_code":{"type":"string","description":"Its branch code."},"deliveries":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Deliveries booked, not cancelled."},"service_calls":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Service calls booked, not cancelled."},"consultations":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Consultations booked, not cancelled."},"completed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of them are done."},"hours_booked":{"type":"string","description":"The hours the visits are booked for, added up, to one decimal place."},"people":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Staff members on at least one of the day's visits."},"unstaffed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Visits with no one on them yet: setAppointmentCrew."}},"required":["date","location_id","location_name","branch_code","deliveries","service_calls","consultations","completed","hours_booked","people","unstaffed"],"additionalProperties":false,"description":"One store's visits on one day, against the people on them."},"WorkloadDue":{"type":"object","properties":{"date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day they come due."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store that serves them."},"location_name":{"type":"string","description":"Its name."},"service_visits":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Care plan visits due."},"shipments":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Auto-ship cycles due."}},"required":["date","location_id","location_name","service_visits","shipments"],"additionalProperties":false,"description":"Subscription cycles coming due on one day at one store."},"ServiceWorkload":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today at location_id, or where the business is."},"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the visits and subscriptions counted."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day."},"open_work_orders":{"type":"object","properties":{"paid":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open work orders the customer pays for."},"warranty":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open work orders under warranty."},"unscheduled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Open work orders with no visit booked from today on."},"by_age":{"type":"array","items":{"$ref":"#/components/schemas/WorkOrderAgeBucket"},"description":"By days open, youngest first."},"oldest":{"type":"array","items":{"$ref":"#/components/schemas/OldestWorkOrder"},"description":"Up to ten, longest open first."}},"required":["paid","warranty","unscheduled","by_age","oldest"],"additionalProperties":false,"description":"Work orders still open now, whatever the period."},"days":{"type":"array","items":{"$ref":"#/components/schemas/WorkloadDay"},"description":"Each store's visits per day from from to to, by date and branch code."},"subscriptions_due":{"type":"array","items":{"$ref":"#/components/schemas/WorkloadDue"},"description":"Active subscriptions whose next cycle falls from from to to, by date and store."},"subscriptions_overdue":{"type":"object","properties":{"service_visits":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Care plan visits whose cycle date has passed with no order made."},"shipments":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Auto-ship cycles whose date has passed with no order made."}},"required":["service_visits","shipments"],"additionalProperties":false,"description":"Active subscriptions already past their next cycle: makeDueSubscriptionOrders catches them up."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["as_of","from","to","open_work_orders","days","subscriptions_due","subscriptions_overdue","next"],"additionalProperties":false,"description":"Open work orders by age, visits per day against people, and subscription cycles coming due."},"StockShrinkRow":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock was at."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"adjustment_reason_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The reason. Null for stock an engine wrote off with none (a trade-in disposed of)."},"adjustment_reason_code":{"description":"A short code for the reason, unique within the business, uppercase letters, digits, _ and -: BROKEN, THEFT. Fixed once the reason exists.","type":["string","null"]},"adjustment_reason_name":{"type":"string","description":"The reason as staff choose it, for example \"Broken or spilled\". \"No reason recorded\" for none."},"units_lost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units lost less units found. A write-down changes what a unit is worth, not how many there are, and counts none. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_lost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost: the value that left stock, less the value found. Write-downs included. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"movements":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many stock movements make it up."}},"required":["location_id","location_name","adjustment_reason_id","adjustment_reason_code","adjustment_reason_name","units_lost","cost_lost","movements"],"additionalProperties":false,"description":"Stock lost or found at one location for one reason."},"StockShrinkByReason":{"type":"object","properties":{"adjustment_reason_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The reason. Null for stock an engine wrote off with none (a trade-in disposed of)."},"adjustment_reason_code":{"description":"A short code for the reason, unique within the business, uppercase letters, digits, _ and -: BROKEN, THEFT. Fixed once the reason exists.","type":["string","null"]},"adjustment_reason_name":{"type":"string","description":"The reason as staff choose it, for example \"Broken or spilled\". \"No reason recorded\" for none."},"units_lost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units lost less units found. A write-down changes what a unit is worth, not how many there are, and counts none. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_lost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost: the value that left stock, less the value found. Write-downs included. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"movements":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many stock movements make it up."},"share_of_shrink":{"description":"This reason's cost_lost as a percent of the total. To one decimal place. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations).","type":["string","null"]}},"required":["adjustment_reason_id","adjustment_reason_code","adjustment_reason_name","units_lost","cost_lost","movements","share_of_shrink"],"additionalProperties":false,"description":"Stock lost or found for one reason, every location."},"StockShrinkByLocation":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock was at."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"units_lost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units lost less units found. A write-down changes what a unit is worth, not how many there are, and counts none. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_lost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost: the value that left stock, less the value found. Write-downs included. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"movements":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many stock movements make it up."},"cost_sold":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods sold from this location in the period cost, less those brought back. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"percent_of_cost_sold":{"description":"cost_lost as a percent of cost_sold: shrink as a share of sales at cost. To one decimal place. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations).","type":["string","null"]}},"required":["location_id","location_name","units_lost","cost_lost","movements","cost_sold","percent_of_cost_sold"],"additionalProperties":false,"description":"Stock lost or found at one location, every reason."},"StockShrink":{"type":"object","properties":{"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day counted."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day counted."},"rows":{"type":"array","items":{"$ref":"#/components/schemas/StockShrinkRow"},"description":"By location, then by the most lost first."},"by_reason":{"type":"array","items":{"$ref":"#/components/schemas/StockShrinkByReason"},"description":"Every location added up, by reason, most lost first."},"by_location":{"type":"array","items":{"$ref":"#/components/schemas/StockShrinkByLocation"},"description":"Every reason added up, by location."},"totals":{"type":"object","properties":{"units_lost":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units lost less units found. A write-down changes what a unit is worth, not how many there are, and counts none. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_lost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost: the value that left stock, less the value found. Write-downs included. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"movements":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many stock movements make it up."},"cost_sold":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods sold in the period cost, less those brought back. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"percent_of_cost_sold":{"description":"cost_lost as a percent of cost_sold. To one decimal place. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations).","type":["string","null"]}},"required":["units_lost","cost_lost","movements","cost_sold","percent_of_cost_sold"],"additionalProperties":false,"description":"Every row added up."},"posted_to":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"ledger_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"ledger_account_code":{"type":"string","description":"The account number or short code, unique within the tenant, for example 1200."},"ledger_account_name":{"type":"string","description":"The account name, unique within the tenant, for example \"Inventory\"."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Debits less credits there. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["ledger_account_id","ledger_account_code","ledger_account_name","amount"],"additionalProperties":false}},{"type":"null"}],"description":"Where the journal entries of these movements posted their cost: Inventory Adjustments, an expense account a reason names, and any cost variance. They add up to totals.cost_lost. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"inventory_ledger_change":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the same journal entries moved the Inventory account by: totals.cost_lost taken off it, so the two always agree. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"returns_not_restocked":{"type":"object","properties":{"units":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units customers brought back that could not be sold again, so never went back on the shelf. A decimal string with up to 3 decimal places, for example \"12.500\"."},"estimated_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost, as their sale was costed (goods returned without a receipt at the cost assumed for the item there). Their cost stays in cost of goods sold, not in the totals above. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."}},"required":["units","estimated_cost"],"additionalProperties":false,"description":"Customer returns thrown away: loss the stock ledger never sees."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["from","to","rows","by_reason","by_location","totals","posted_to","inventory_ledger_change","returns_not_restocked","next"],"additionalProperties":false,"description":"Stock lost, damaged or found in a period, by location and reason, against the cost of what was sold."},"StockPerformance":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Its category."},"category_name":{"description":"Its category's name.","type":["string","null"]},"brand_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Its brand."},"brand_name":{"description":"Its brand's name.","type":["string","null"]},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location, with by_location. Null for every location added up."},"location_name":{"description":"Its name. Null without by_location.","type":["string","null"]},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units on hand now. A decimal string with up to 3 decimal places, for example \"12.500\"."},"value_on_hand":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"average_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"value_on_hand per unit on hand. Null when none are on hand. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"units_sold":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units handed over to customers from here in the window, less those brought back. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_sold":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What those units cost. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"turns_annualized":{"description":"How many times a year the stock sells through at the window's pace: cost_sold over the average value on hand in the window, times 365 over window_days, to two decimal places. Null when nothing was on hand. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations).","type":["string","null"]},"days_of_supply":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many days what is on hand lasts at the window's pace of sales. Null when none sold."},"last_received_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When stock last came in (received, opening, counted or transferred in)."},"last_sold_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When a unit was last handed over from here."},"oldest_on_hand_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When the oldest stock still on hand arrived."},"is_dead":{"type":"boolean","description":"True when stock is on hand, none sold in not_sold_days, and the oldest on hand arrived before that."}},"required":["item_id","sku","item_name","category_id","category_name","brand_id","brand_name","location_id","location_name","quantity_on_hand","value_on_hand","average_cost","units_sold","cost_sold","turns_annualized","days_of_supply","last_received_on","last_sold_on","oldest_on_hand_on","is_dead"],"additionalProperties":false,"description":"How one item's stock is selling, at one location or all."},"StockPerformanceTotals":{"type":"object","properties":{"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many rows match, listed or not."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units on hand, every matching row. A decimal string with up to 3 decimal places, for example \"12.500\"."},"value_on_hand":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"units_sold":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units sold in the window, every matching row. A decimal string with up to 3 decimal places, for example \"12.500\"."},"cost_sold":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"dead_rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows that are dead stock."},"dead_value":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the dead stock cost. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."}},"required":["rows","quantity_on_hand","value_on_hand","units_sold","cost_sold","dead_rows","dead_value"],"additionalProperties":false,"description":"Every matching row added up."},"StockPerformanceReport":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today where the business is. Windows end on each location's today."},"window_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The days sales are counted over, ending today."},"not_sold_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The days without a sale that make stock dead."},"rows":{"type":"array","items":{"$ref":"#/components/schemas/StockPerformance"},"description":"The items, sorted as asked, up to limit."},"totals":{"$ref":"#/components/schemas/StockPerformanceTotals"},"inventory_ledger_balance":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The Inventory account's balance in the journal, at location_id when one is named, which totals.value_on_hand equals. Null when the rows are narrowed otherwise. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["as_of","window_days","not_sold_days","rows","totals","inventory_ledger_balance","next"],"additionalProperties":false,"description":"Stock on hand with its value, sales pace, turns, days of supply and dead stock."},"ReplenishmentLine":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where it is needed."},"location_name":{"type":"string","description":"Its name."},"vendor_sku":{"description":"The vendor's own code for the item, as it must appear on an order to them.","type":["string","null"]},"pack_text":{"description":"What the vendor sells the item in, when it is not one unit: case, pallet, drum. Lowercase. NULL when the vendor sells single units.","type":["string","null"]},"pack_size":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How many of the item's unit (unit_text) are in one of the vendor's packs: 6 for a case of six bottles, 40 for a pallet of 40 bags, 50 for a 50 lb drum of a chemical sold by the pound. A whole number for an item counted in whole units."},"pack_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,4})?$","description":" A decimal string with up to 4 decimal places, for example \"12.5000\"."},{"type":"null"}],"description":"What the vendor charges for one pack, before freight and tax. Goods received in whole packs go into stock at exactly this per pack. NULL when the vendor quotes per unit: a pack then costs cost times pack_size. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units on hand there. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_committed":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units sold there and not yet handed over. A decimal string with up to 3 decimal places, for example \"12.500\"."},"quantity_on_order":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units ordered from vendors for there and not yet received. A decimal string with up to 3 decimal places, for example \"12.500\"."},"daily_rate":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units a day expected to go, from the basis: units sold over rate_days. A decimal string with up to 3 decimal places, for example \"12.500\"."},"rate_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The days daily_rate is taken over: window_days for basis recent, cover_days for last_year. For basis recent it is fewer when the item first arrived or sold at the location inside the window, so a new item's few days of sales are not spread over days it was not stocked, but never fewer than 7."},"short_history":{"type":"boolean","description":"True when rate_days is shorter than the window because the item is new at the location: the pace rests on little history, so weigh the suggestion."},"minimum_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The reorder point: when what the location holds, with what is on order and less what is promised to customers, is at or below this, bring it back up to par. NULL: bring it back as soon as it is below par. Less than par_quantity. Null when it keeps none."},"par_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The quantity the location is brought back up to: order up to. NULL once the location no longer keeps a level for the item. Null when it keeps no par level there."},"need_for_pace":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units the pace calls for to cover cover_days: daily_rate times cover_days, plus committed, less on hand and on order. A decimal string with up to 3 decimal places, for example \"12.500\"."},"need_for_par":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units that bring it back up to par when what it holds, with what is on order and less what is committed, is at or below its minimum (or below par, with no minimum). 0 otherwise. A decimal string with up to 3 decimal places, for example \"12.500\"."},"reasons":{"type":"array","items":{"type":"string","enum":["par_level","sales_pace"]},"description":"Why it is suggested. par_level: it fell to its minimum (or below par), and need_for_par counts. sales_pace: it sells fast enough to need more over cover_days, and need_for_pace counts."},"need":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units to order: the larger of need_for_pace and need_for_par, or need_for_par alone where the item keeps a par level and the business orders such items to par only (tenant_policy.par_level_replenishment). A decimal string with up to 3 decimal places, for example \"12.500\"."},"suggested_packs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Packs to order, rounding need up. Null when the vendor sells it by the unit."},"suggested_quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units to order: whole packs, or need rounded up to whole units. A decimal string with up to 3 decimal places, for example \"12.500\"."},"suggested_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What they cost at the vendor's price, or the last cost paid when it has none. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."},"available_elsewhere":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units the other premises hold beyond what is committed and their own par level, which a transfer could bring. Vans' stock is not counted. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","sku","item_name","location_id","location_name","vendor_sku","pack_text","pack_size","pack_cost","quantity_on_hand","quantity_committed","quantity_on_order","daily_rate","rate_days","short_history","minimum_quantity","par_quantity","need_for_pace","need_for_par","reasons","need","suggested_packs","suggested_quantity","suggested_cost","available_elsewhere"],"additionalProperties":false,"description":"One item to order for one location."},"ReplenishmentTransferLine":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity_on_hand":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units on hand at the location to restock. A decimal string with up to 3 decimal places, for example \"12.500\"."},"minimum_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"The reorder point: when what the location holds, with what is on order and less what is promised to customers, is at or below this, bring it back up to par. NULL: bring it back as soon as it is below par. Less than par_quantity."},"par_quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"The quantity the location is brought back up to: order up to. NULL once the location no longer keeps a level for the item."},"need":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units that bring it back up to par. A decimal string with up to 3 decimal places, for example \"12.500\"."},"suggested_quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units to move: need, in whole units for an item sold whole. A decimal string with up to 3 decimal places, for example \"12.500\"."},"available_at_source":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units the location it comes from holds and has not promised to customers. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","sku","item_name","quantity_on_hand","minimum_quantity","par_quantity","need","suggested_quantity","available_at_source"],"additionalProperties":false,"description":"One item to move to a location to bring it back to par."},"ReplenishmentTransfer":{"type":"object","properties":{"from_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where it comes from: the vehicle's home."},"from_location_name":{"type":"string","description":"Its name."},"to_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The vehicle to restock."},"to_location_name":{"type":"string","description":"Its name."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/ReplenishmentTransferLine"},"description":"What to move, by SKU."}},"required":["from_location_id","from_location_name","to_location_id","to_location_name","lines"],"additionalProperties":false,"description":"What to move to one vehicle from its home store: a van is restocked by transfer, never ordered for."},"ReplenishmentVendor":{"type":"object","properties":{"vendor_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The vendor to order from: the preferred one. Null when none is set up."},"vendor_name":{"type":"string","description":"Its name, or 'No vendor'."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/ReplenishmentLine"},"description":"What to order from it, by location and SKU."},"suggested_cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The lines' suggested_cost added up. Null unless the key holds cost.view at every location the figure counts (everywhere, for a figure across locations)."}},"required":["vendor_id","vendor_name","lines","suggested_cost"],"additionalProperties":false,"description":"What to order from one vendor."},"Replenishment":{"type":"object","properties":{"as_of":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Today where the business is."},"basis":{"type":"string","enum":["recent","last_year"],"description":"Where daily_rate comes from."},"cover_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The days the order is to cover."},"vendors":{"type":"array","items":{"$ref":"#/components/schemas/ReplenishmentVendor"},"description":"What premises should order: one group per vendor, by name, with No vendor last."},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/ReplenishmentTransfer"},"description":"What vehicles need from their home stores, by par level alone: one group per vehicle. Empty when vendor_id is sent."},"lines":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many lines in all, ordered and moved."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["as_of","basis","cover_days","vendors","transfers","lines","next"],"additionalProperties":false,"description":"What to order before it runs out, rounded to packs, by vendor."},"Sale":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["hand_over","return","receiptless_return"],"description":"hand_over: goods or services given to the customer. return: goods the customer brought back, returned on the order they were sold on. receiptless_return: goods taken back with no order to return them on (returnGoodsWithoutReceipt); it has no order or line."},"fulfilled_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date it happened, at the store that made the sale (the order's location), wherever the goods left from: that store's day carries it. Sales reports, the day close, the journal and quota periods use this date. Calculated."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order. Taken from the line. Null for a receiptless_return."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically. Null for a receiptless_return."},"sales_order_line_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The line being handed over or returned. Null for a receiptless_return."},"line_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Position of the line on the order, starting at 1. Assigned automatically. Null for a receiptless_return."},"receiptless_return_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically. Null unless kind is receiptless_return."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"description":"The customer's name, or null for an anonymous counter sale.","type":["string","null"]},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item sold. Fixed once the line exists."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"category_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The category the item is filed under."},"category_name":{"description":"The category's name, or null when the item is filed under none.","type":["string","null"]},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many: positive when handed to the customer, negative when returned. A line cannot be handed over beyond its quantity, or returned beyond what was handed over. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Agreed price per unit, before discount and tax. Defaults to list_price. On a line sold by the pack it is pack_price divided by pack_size, to the cent, for reading only: the line is priced by pack_price. From the order line, before any discount on it: revenue_amount is what was charged. For a receiptless_return, what each unit was valued at. A decimal string with up to 2 decimal places, for example \"12.50\"."},"discount_amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What a discount took off the goods at unit_price (pack_price on a line sold by the pack): unit_price times quantity, less revenue_amount, signed as revenue_amount, so negative on a return. A line handed over in parts can show a cent either way. 0.00 for a receiptless_return. A decimal string with up to 2 decimal places, for example \"12.50\"."},"price_list_code":{"description":"The code of the price list the order line's list price came from (getSalesOrder shows how it was priced). Null for the item's or pack's own price, and for a receiptless_return.","type":["string","null"]},"revenue_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Revenue earned by this hand-over, before tax: the line's price in proportion to the quantity. Negative for a return. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax_amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Sales tax that fell due on this hand-over. Negative for a return. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost_amount":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods cost the tenant, from the stock ledger. Negative for a return. Zero for an item with no stock. Calculated. Null without cost.view at the store that made the sale (sold_at_location_id)."},"margin":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"revenue_amount less cost_amount. Null without cost.view at the store that made the sale (sold_at_location_id)."},"serialized_unit_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The individual unit, for a serial-tracked item. Taken from the line."},"serial_number":{"description":"The manufacturer's serial number, uppercase, unique within the item.","type":["string","null"]},"lots":{"type":"array","items":{"$ref":"#/components/schemas/MovedLot"},"description":"For an item tracked by lot: each lot handed over, or come back on a return, and how many, signed as quantity is. Empty otherwise."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location the stock leaves from or comes back to. Defaults to where the line's serialized unit is, else the order's location. Goods the customer carries out (an order with no delivery site) are handed over only at a location in the order's tax area. Income, tax and cost of goods count for the order's location whatever this is; only the stock is this location's."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"sold_at_location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store the sale belongs to: the order's location, or where goods came back without a receipt. Its income, tax and cost of goods are posted there."},"sold_at_location_name":{"type":"string","description":"That store's name."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who handed the goods over or took them back."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"note":{"description":"Anything worth recording, for example why goods were returned.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","kind","fulfilled_on","sales_order_id","order_number","sales_order_line_id","line_number","receiptless_return_number","customer_id","customer_name","item_id","sku","item_name","category_id","category_name","quantity","unit_price","discount_amount","price_list_code","revenue_amount","tax_amount","cost_amount","margin","serialized_unit_id","serial_number","lots","location_id","location_name","sold_at_location_id","sold_at_location_name","staff_member_id","staff_member_name","note","created_at"],"additionalProperties":false,"description":"Goods or services on one order line handed to the customer, or returned by them. This is the moment a sale counts: revenue, tax, cost and commission all date from it. Rows are only ever added; a return is a new row with a negative quantity. As read for history and reports, naming the order, customer and item."},"SaleTotals":{"type":"object","properties":{"hand_overs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many hand-overs match."},"returns":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many returns match, with or without a receipt."},"receiptless_returns":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many of the returns were taken back without a receipt."},"quantity":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":"Units handed over less units returned. Meaningful when filtered to one item. A decimal string with up to 3 decimal places, for example \"12.500\"."},"lot_quantity":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"When filtered by lot: units of that lot handed over less units of it returned, which is how much of the lot customers still hold. Null otherwise."},"revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue before tax, net of returns. A decimal string with up to 2 decimal places, for example \"12.50\"."},"returned_revenue":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Revenue given back on returns, as a positive amount. Already taken off revenue. A decimal string with up to 2 decimal places, for example \"12.50\"."},"tax":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Sales tax, net of returns. A decimal string with up to 2 decimal places, for example \"12.50\"."},"cost":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"What the goods cost, net of returns. Null unless the key holds cost.view at every store whose sales are counted."},"margin":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"revenue less cost. Null unless the key holds cost.view at every store whose sales are counted."}},"required":["hand_overs","returns","receiptless_returns","quantity","lot_quantity","revenue","returned_revenue","tax","cost","margin"],"additionalProperties":false,"description":"Totals over every record that matches the filters, on all pages, not just this one."},"SalePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Sale"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."},"totals":{"$ref":"#/components/schemas/SaleTotals"}},"required":["data","next_cursor","totals"],"additionalProperties":false,"description":"One page of hand-overs and returns, the cursor for the next, and totals over them all."},"HeldRole":{"type":"object","properties":{"staff_role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The grant itself. Name it to take the role away."},"role_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The role held."},"role_name":{"type":"string","description":"The role's name, unique within the tenant."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The one location the role applies at. NULL when it applies everywhere."},"location_name":{"description":"The location's name, or null when held everywhere.","type":["string","null"]},"permissions":{"type":"array","items":{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"],"description":"An action the system can refuse. What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"description":"What the role allows, wherever it is held."}},"required":["staff_role_id","role_id","role_name","location_id","location_name","permissions"],"additionalProperties":false,"description":"One staff member holding one role, either everywhere or at one location. A store manager is a manager at their own store only."},"StaffMember":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"given_name":{"type":"string","minLength":1,"maxLength":200,"description":"First name."},"family_name":{"type":"string","minLength":1,"maxLength":200,"description":"Last name."},"email":{"description":"Work email address, lowercase, unique within the tenant.","type":["string","null"]},"telephone":{"description":"Work or mobile phone number in E.164 form, for example +12085550100.","type":["string","null"]},"job_title":{"description":"Role as the business describes it, for example \"Service Technician\".","type":["string","null"]},"home_location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location this person normally works from. NULL for staff who are not tied to one."},"commission_plan_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The commission plan this person is paid on. NULL for staff who earn no commission."},"is_active":{"type":"boolean","description":"False once the person has left. Former staff stay so past sales and work orders still name them."},"commission_plan_name":{"description":"The commission plan's name, or null when they earn none.","type":["string","null"]},"home_location_name":{"description":"The home location's name, or null when there is none.","type":["string","null"]},"roles":{"type":"array","items":{"$ref":"#/components/schemas/HeldRole"},"description":"The roles this person holds now. Taken-away grants are not listed."},"can_sign_in":{"type":"boolean","description":"True once the person has set a password and can sign in (signIn). False until then: a manager issues them a sign-in code (issueSignInCode) to set one."},"has_pin":{"type":"boolean","description":"True once the person has set a PIN (setPin), with which they switch in on the store's counter computers."},"pin_locked":{"type":"boolean","description":"True after five wrong PINs in a row: switching in with the PIN is refused on every device until the person signs in with their password (signIn) or a manager clears it (clearPin). It never lifts by itself."},"you_manage_sign_in":{"type":"boolean","description":"Whether the key asking may issue this person a sign-in code (issueSignInCode), lift their sign-in lock (clearSignInLock), and unlock or clear their PIN (unlockPin, clearPin): it holds staff.manage where they are based and everything they hold, and is not a session switched into with a PIN."},"sign_in_locked_until":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"Until when their sign-in is locked, after ten wrong passwords or codes within fifteen minutes; it lifts by itself. Null when it is not locked."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","given_name","family_name","email","telephone","job_title","home_location_id","commission_plan_id","is_active","commission_plan_name","home_location_name","roles","can_sign_in","has_pin","pin_locked","you_manage_sign_in","sign_in_locked_until","created_at","updated_at"],"additionalProperties":false,"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person."},"StaffPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StaffMember"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of staff members and the cursor for the next."},"Role":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The role's name, unique within the tenant."},"description":{"description":"Who the role is for.","type":["string","null"]},"permissions":{"maxItems":50,"type":"array","items":{"type":"string","enum":["order.discount","order.cancel","tax.override","credit.manage","payment.refund","stock.adjust","catalog.manage","purchase.manage","cost.view","commission.view","ledger.post","books.close","policy.manage","staff.manage","warranty.manage","stock.transfer","order.credit","site.tax_area","order.return","financing.manage","delivery.override","cash.manage","giftcard.manage","sales.view","labor.manage","card.manage","message.send","price_list.manage","trade_in.appraise","device.manage","schedule.manage","subscription.manage","receivables.view","message.view","lead.manage"],"description":"An action the system can refuse. What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"description":"What holders may do. order.discount: discount a sale. order.cancel: cancel an order, or take a sale already under way off the customer it names. tax.override: tax a sale other than the way it would be by default: mark a customer tax-exempt, exempt an order, tax it in another area, or date it on a day other than today, which picks the rates. credit.manage: give a customer more credit: raise or remove their credit limit, or lengthen their payment terms; and charge customers for paying late: set the business's finance charges, make a month's (assessFinanceCharges) and waive one. payment.refund: refund a payment. stock.adjust: adjust stock outside buying and selling. catalog.manage: change items, prices and vendors. purchase.manage: raise, change, send, close and cancel purchase orders, send goods back to the vendor, receive goods at a cost other than the order's, and add freight or prep paid afterwards to what goods cost (a landed cost). cost.view: see costs and margins. commission.view: see everyone's commissions, not just one's own. ledger.post: post and reverse manual journal entries, and keep floor-plan financing: lenders, units financed and payments to lenders. books.close: close or reopen an accounting period. policy.manage: change tenant policy. staff.manage: manage staff, roles, commission plans and quotas. warranty.manage: record a manufacturer's answer to a warranty claim (approved, denied) and its payment, which post what the manufacturer owes; set each manufacturer's claim terms; choose what happens to a failed part other than its terms say; and, held at the job's location, mark a job as warranty work or take the mark off, and record a warranty on equipment or change what it covers. stock.transfer: move stock from one location to another; held at the location it leaves. order.credit: change who is credited with a sale after it is written up. site.tax_area: name the tax area of a delivery site, which decides the tax on what is delivered there. order.return: take back goods a customer returns, which reverses their revenue, tax and commission; held at the location they come back to. financing.manage: set up the lenders the business sells on, their promotions, dealer fees and funding terms, and record what a lender paid out or took back, which posts the money in, what the lender still owes and the dealer fee. delivery.override: complete a delivery with nobody there to sign, and send a crew to a site not shown ready when the business requires readiness. cash.manage: set up registers and the reasons cash is paid in or out; see what a drawer should hold when counts are blind; reconcile a drawer closed over or short; pay cash in or out of a drawer beyond the business's limit; and put cash in the safe, count it and take it to the bank. Held at the location. giftcard.manage: void a gift card sale the same day, unblock a card or clear one locked by wrong PINs, replace a lost or damaged card, and give promotional cards, each held at the location; and, held everywhere, write off what is left on a card (breakage or unclaimed property). sales.view: see every sale at the location: its orders, payments and hand-overs and returns, and the reports added up from them; without it a key sees only sales on orders its staff member is credited on, and one order or customer at a time. Held at the location. labor.manage: record or correct another person's time on a job, record time, parts and photos dated further back than the business allows, complete a service visit with nobody there to sign where the business requires a signature, drop a checklist a job requires, and move a job to another location (held at both). Held at the job's location. card.manage: register and retire the card readers the business takes cards on through its card processor, and catch up with the processor (syncCardProcessor). message.send: send the messages that are due through the business's connected email and text services (sendDueMessages), and record what an outside sender did with a message (updateMessage). price_list.manage: keep price lists: create, change and retire them, put prices on them, say which stores they apply at, give a store its regular list, and put a customer on a list or take them off one (a trade, employee or contract price is a price concession). Held everywhere. trade_in.appraise: take a customer's old unit in toward a sale and set what it is allowed at (addTradeIn, updateTradeIn), or call the trade off (cancelTradeIn). Held at the order's location. device.manage: enroll a store's counter computer as a device people switch in on with their PIN (enrollDevice), see the devices enrolled there, and revoke one. Held at the location. schedule.manage: see every sold order at the location still waiting for a delivery to be booked (listDeliveriesToBook), without seeing its sales otherwise, and record or withdraw time off for the people who work from there (a staff member records their own without it). Held at the location. subscription.manage: start a care plan or auto-ship for a customer (createSubscription), held at the plan's location; and run the daily making of the orders that are due (makeDueSubscriptionOrders), which makes and reports only the plans at the locations where it is held. receivables.view: see the whole business's list of customers who owe money or hold a deposit, with what is late and their credit terms (listCustomerBalances). Held everywhere. message.view: read the messages sent to customers and held back (listMessages). A payment link in a message is shown whole only to a key that also holds message.send, which sends it; to anyone else its secret part is hidden. Held everywhere. lead.manage: see every lead at the location and the pipeline report for it, hand leads out to salespeople or take them back, and keep the business's lead sources (held everywhere for those). Without it a salesperson sees the leads they own or took down, and any one lead in front of them. Held at the location."},"max_discount_percent":{"description":"The most its holders may take off a line without order.discount, as a percent of what the line comes to at its list price (what the store's and the customer's price lists give it, else the item's own price): a discount, a lower price, or both. A lower price a price list gives is not a discount. 10 lets a salesperson give up to 10% off on their own; more needs order.discount. NULL (the default) allows no discount without order.discount. A key holding several roles where the sale is made may give the largest of their allowances. Giving or changing an allowance needs order.discount for the whole business, as giving that permission does.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the role is retired. Its holders lose its permissions."},"holders":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many staff members hold this role now, anywhere."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","description","permissions","max_discount_percent","is_active","holders","created_at","updated_at"],"additionalProperties":false,"description":"A named set of permissions the tenant defines, for example \"Sales Manager\". Staff and API keys hold roles; what someone may do is the union of the permissions of the roles they hold. Anything no permission names, everyone may do."},"RolePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Role"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of roles and the cursor for the next."},"StoreCreditEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["goodwill","refund","redemption","receiptless_return"],"description":"goodwill: credit given without anything coming back, as goodwill or as a price adjustment on goods the customer keeps; it reduces sales income through Sales Allowances. refund: a refund on an order paid out as store credit. redemption: credit spent paying for an order. receiptless_return: credit given for goods taken back without a receipt. Only goodwill is written directly."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The customer whose credit changed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"Positive when credit is given, negative when it is spent. Spending never takes the balance below zero. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance_after":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The customer's store credit balance after this entry, in the order entries were applied (sequence). Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"entered_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date, at the location. Calculated."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the credit was given or spent. A goodwill credit counts against this location's sales."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"sales_order_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The order the credit was spent on or refunded from. Null for any other kind."},"order_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The order's number. Null for any other kind."},"receiptless_return_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The return without a receipt the credit was given for. Set for receiptless_return only."},"receiptless_return_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The number staff and the customer quote, unique within the tenant and assigned automatically. Null for any other kind."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who gave the credit or took it in payment."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"note":{"description":"Why. Required for a goodwill credit: what it is for, for example \"scratched cover, kept at 40 off\".","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","kind","customer_id","customer_name","amount","balance_after","entered_on","location_id","location_name","sales_order_id","order_number","receiptless_return_id","receiptless_return_number","staff_member_id","staff_member_name","note","created_at"],"additionalProperties":false,"description":"One change in a customer's store credit: credit given (a refund to store credit, a return without a receipt, a goodwill credit) or spent (a payment with store credit). This is the store credit ledger: rows are only ever added. The application writes only goodwill credits; the others are written by the payment or return that caused them."},"StoreCreditEntryPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StoreCreditEntry"},"description":"Records on this page, newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of store credit given and spent."},"Subscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"What the arrangement is called, for example \"Valet Care Diamond\" or \"Monthly chemicals\"."},"kind":{"type":"string","enum":["service_visit","shipment"],"description":"service_visit: each cycle is a visit by a technician. shipment: each cycle is goods sent or set aside for the customer."},"status":{"type":"string","enum":["active","paused","cancelled"],"description":"active: cycles are generated when due. paused: nothing is generated until it is made active again, and cycles missed meanwhile are skipped. cancelled: over, for good."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who it is for and who is billed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where the visit happens or the goods go: one of the customer's own sites."},"site_name":{"description":"The site's name, or null when the subscription has no site.","type":["string","null"]},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment a service visit looks after, which must be the customer's own."},"equipment_name":{"description":"The equipment's name, or null when it is not for a piece of equipment.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location that supplies it. Each cycle's order is made at this location."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"interval_unit":{"type":"string","enum":["day","week","month"],"description":"The unit of the rhythm: day, week or month. Fixed once a cycle has been generated."},"interval_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many units between cycles: 2 with week is every two weeks. Fixed once a cycle has been generated."},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day of the arrangement, and the date the first cycle falls due. Every later cycle is counted from it. Fixed once a cycle has been generated."},"ends_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The last day of the current term. NULL for an arrangement with no end. Extending a term that has lapsed resumes it from today: the cycles missed meanwhile are skipped, not billed."},"auto_renew":{"type":"boolean","description":"True to extend the term by renewal_months when it runs out, instead of stopping."},"renewal_months":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many months each renewal adds to the term when auto_renew is on: 12 for a yearly plan, 3 for quarterly, 1 for monthly. A term that ends the day before a whole number of months from starts_on keeps doing so: renewing it never drifts toward the 28th."},"lead_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days before a cycle falls due its order may be made, so a visit can be scheduled or goods picked ahead of time. 0 makes the order on the due date."},"cycle_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many cycles have been generated or skipped. Calculated."},"next_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The date the next cycle falls due: starts_on plus cycle_count intervals. NULL when the term has ended or the subscription is cancelled. Calculated, never sent."},"order_can_be_made_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The first day the next cycle's order can be made: next_due_on less lead_days."},"notes":{"description":"Free-form notes for staff.","type":["string","null"]},"lines":{"type":"array","items":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The item supplied each cycle: a service visit, a chemical."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"quantity":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How many each cycle. 0 takes the item out of the subscription. A decimal string with up to 3 decimal places, for example \"12.500\"."},"unit_price":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"The price agreed for this subscription. NULL charges the item's price at the time of each cycle."},"description":{"description":"What the order line should say. NULL uses the item's name.","type":["string","null"]}},"required":["item_id","sku","item_name","quantity","unit_price","description"],"additionalProperties":false},"description":"What each cycle includes. Lines with quantity 0 are left out."},"orders":{"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order made for a cycle."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"cycle_due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The cycle the order is for."},"status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."}},"required":["sales_order_id","order_number","cycle_due_on","status"],"additionalProperties":false},"description":"The orders made so far, one per cycle, latest first. At most 24 are listed."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","kind","status","customer_id","customer_name","site_id","site_name","equipment_id","equipment_name","location_id","location_name","interval_unit","interval_count","starts_on","ends_on","auto_renew","renewal_months","lead_days","cycle_count","next_due_on","order_can_be_made_on","notes","lines","orders","next","custom_fields","created_at","updated_at"],"additionalProperties":false,"description":"A standing arrangement to supply a customer on a schedule: a recurring service visit, or a regular shipment of supplies. Each cycle becomes a sales order."},"SubscriptionPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Subscription"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of subscriptions."},"SubscriptionOrdersMade":{"type":"object","properties":{"made":{"type":"array","items":{"type":"object","properties":{"subscription_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The subscription."},"subscription_name":{"type":"string","description":"What the arrangement is called, for example \"Valet Care Diamond\" or \"Monthly chemicals\"."},"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order made for its cycle."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"cycle_due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The cycle the order is for."}},"required":["subscription_id","subscription_name","sales_order_id","order_number","cycle_due_on"],"additionalProperties":false},"description":"The orders made by this call, one per cycle that was ready."},"failed":{"type":"array","items":{"type":"object","properties":{"subscription_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The subscription."},"subscription_name":{"type":"string","description":"What the arrangement is called, for example \"Valet Care Diamond\" or \"Monthly chemicals\"."},"cycle_due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The cycle that could not be ordered."},"reason":{"type":"string","description":"Why, in the database's words. Fix it and run again."}},"required":["subscription_id","subscription_name","cycle_due_on","reason"],"additionalProperties":false},"description":"Subscriptions whose cycle could not be ordered. The others were ordered regardless."},"more":{"type":"boolean","description":"True when the call stopped before every ready subscription was done, to stay within the time a request may take. What is listed in made is ordered and stays ordered; call again for the rest."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["made","failed","more","next"],"additionalProperties":false,"description":"What a run of the subscriptions made."},"TaxClass":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The class name, unique within the tenant."},"description":{"description":"What belongs in the class.","type":["string","null"]},"is_default":{"type":"boolean","description":"True for the one class used by items that name no class of their own."},"is_active":{"type":"boolean","description":"False once the class is retired."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","description","is_default","is_active","created_at","updated_at"],"additionalProperties":false,"description":"A group of items taxed the same way, for example ordinary goods, or repair labor that a state does not tax. Rates are set per class. A new tenant has one class, Standard."},"TaxClassPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TaxClass"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of tax classes."},"TaxRate":{"type":"object","properties":{"tax_rate_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The rate itself."},"tax_class_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The class of item taxed."},"tax_class_name":{"type":"string","description":"The class name, unique within the tenant."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The rate as a fraction: 0.06 is six percent. A decimal string with up to 5 decimal places, for example \"12.50000\"."},"effective_from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first order date the rate applies to. The rate in force is the one with the latest effective_from on or before the order date. A date, YYYY-MM-DD."}},"required":["tax_rate_id","tax_class_id","tax_class_name","rate","effective_from"],"additionalProperties":false,"description":"What one jurisdiction charges on one class of item, starting on a date. A rate change is a new row with a later date, so past sales keep the rate they were taxed at. No row means the class is not taxed there."},"TaxJurisdiction":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The authority's name, unique within the tenant."},"is_active":{"type":"boolean","description":"False once the tenant no longer collects for it."},"trade_in_credit":{"type":"string","enum":["none","same_class","all"],"description":"Whether this jurisdiction taxes a sale less what the customer was allowed for an old unit traded in toward it (trade_in, 0008): none, the whole price is taxed; same_class, the allowance comes off the taxed price of the order's lines in the same tax class as the item the old unit comes in as (give spas their own class for a like-kind rule); all, it comes off every taxed line. The allowance is shared over the lines in proportion to their price, never below nothing. A line taxed less this way is handed over only once the trade is received. Which applies is the business's to find out for each authority: none until it says otherwise."},"rates":{"type":"array","items":{"$ref":"#/components/schemas/TaxRate"},"description":"Every rate it has charged or will charge, by class, latest effective_from first."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","is_active","trade_in_credit","rates","created_at","updated_at"],"additionalProperties":false,"description":"An authority the tenant collects sales tax for and reports to, for example \"State of Idaho\" or \"City of Hailey\". Tax is recorded per jurisdiction so each can be paid what it is owed."},"TaxJurisdictionPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TaxJurisdiction"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of tax jurisdictions."},"TaxArea":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The area's name, unique within the tenant."},"is_active":{"type":"boolean","description":"False once the area is retired."},"jurisdictions":{"type":"array","items":{"type":"object","properties":{"tax_jurisdiction_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"A jurisdiction that taxes sales in the area."},"name":{"type":"string","description":"The authority's name, unique within the tenant."}},"required":["tax_jurisdiction_id","name"],"additionalProperties":false},"description":"The jurisdictions whose taxes apply in the area."},"rates_today":{"type":"array","items":{"type":"object","properties":{"tax_class_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The class of item taxed."},"tax_class_name":{"type":"string","description":"The class name, unique within the tenant."},"rate":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,5})?$","description":"The combined rate of every jurisdiction in the area, as a fraction. A decimal string with up to 5 decimal places, for example \"12.50000\"."}},"required":["tax_class_id","tax_class_name","rate"],"additionalProperties":false},"description":"What a sale taxed here today pays, by class. A class not listed is not taxed here."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","is_active","jurisdictions","rates_today","created_at","updated_at"],"additionalProperties":false,"description":"A place, for tax purposes: the set of jurisdictions whose taxes all apply there. \"Hailey\" is the State of Idaho plus the City of Hailey. Locations and delivery sites each name their area."},"TaxAreaPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TaxArea"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of tax areas."},"LeadSource":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"code":{"type":"string","description":"What staff and forms quote, uppercase, unique within the business: WALKIN, WEB, HOMESHOW-2026."},"name":{"type":"string","description":"The name people see, unique within the business: \"Boise Home & Garden Show 2026\"."},"kind":{"type":"string","enum":["walk_in","phone","web","show","referral","advertising","other"],"description":"What sort of source it is, so reports can add up across them: walk_in, phone, web, show (a home show or event), referral, advertising, other."},"is_active":{"type":"boolean","description":"False once it is no longer used (a show that is over). Its leads keep it."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","code","name","kind","is_active","created_at","updated_at"],"additionalProperties":false,"description":"Where the business's leads come from, as it names them: a walk-in, a phone call, the website's form, the spring home show. The pipeline report adds up by it, so a show worth repeating stands out. schema.org: Thing."},"LeadSourcePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LeadSource"},"description":"Records on this page, by name."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of the business's lead sources."},"LeadSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"lead_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the business and assigned automatically."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is asking: a customer already, or a prospect (customer.is_prospect) made when they first asked. Fixed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"is_prospect":{"type":"boolean","description":"True for someone who has asked about buying (a lead) and not yet bought. A prospect is a customer like any other (contact points, consent, sites, the counter's lookup) until their first order is agreed, when the system clears it and tells customer.created. Set only when the customer is first recorded."},"telephone":{"description":"Their primary (or any active) phone number, or null.","type":["string","null"]},"email":{"description":"Their primary (or any active) email address, or null.","type":["string","null"]},"lead_source_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the lead came from."},"lead_source_name":{"type":"string","description":"The name people see, unique within the business: \"Boise Home & Garden Show 2026\"."},"lead_source_kind":{"type":"string","enum":["walk_in","phone","web","show","referral","advertising","other"],"description":"What sort of source it is, so reports can add up across them: walk_in, phone, web, show (a home show or event), referral, advertising, other."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The showroom the lead belongs to: where they came in, or the store a form or call was for. Reports by store count it here."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The salesperson who owns it and owes its follow-ups. NULL while no one does, waiting for a manager to hand it out. Who it starts with is the business's choice (tenant_policy.lead_owner). Giving it to someone else needs lead.manage at its store."},"staff_member_name":{"description":"The owner's name, or null while no one owns it.","type":["string","null"]},"captured_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took it down. NULL for a lead from a form or another system."},"captured_by_staff_member_name":{"description":"Who took it down, or null.","type":["string","null"]},"interest":{"type":"string","description":"What they are after, in their words: \"6-person spa for the back deck, wants lounger, asking about 0% financing\"."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The model they are most interested in, when it is one the business sells."},"item_name":{"description":"That model's name, or null.","type":["string","null"]},"stage":{"type":"string","enum":["new","contacted","visited","quoted","won","lost"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages."},"lost_reason":{"anyOf":[{"type":"string","enum":["price","bought_elsewhere","financing","not_ready","no_response","other"]},{"type":"null"}],"description":"Why it was lost: price, bought_elsewhere (a competitor sold them one), financing (could not get it), not_ready (not this year), no_response (stopped answering), other (say what in lost_note). Set only for a lost lead."},"lost_note":{"description":"More on why it was lost: who they bought from, what price beat ours.","type":["string","null"]},"last_activity_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When anything was last logged on it. Set by the system. A lead quiet for tenant_policy.lead_stale_days is going cold."},"days_quiet":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Whole days since anything was last logged on it."},"going_cold":{"type":"boolean","description":"True for an open lead quiet for the business's lead_stale_days or more (getPolicy)."},"next_follow_up_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When its earliest open follow-up is due, or null when none is set: a lead with no next step."},"follow_up_overdue":{"type":"boolean","description":"True when an open follow-up was due before today at its store."},"booked_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the orders linked to it that were agreed (open or completed) come to, tax included: the booked value it won. A decimal string with up to 2 decimal places, for example \"12.50\"."},"delivered_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Of those orders, what has been handed over, tax included: the delivered value. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was won or lost. Set by the system."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","lead_number","customer_id","customer_name","customer_number","is_prospect","telephone","email","lead_source_id","lead_source_name","lead_source_kind","location_id","location_name","staff_member_id","staff_member_name","captured_by_staff_member_id","captured_by_staff_member_name","interest","item_id","item_name","stage","lost_reason","lost_note","last_activity_at","days_quiet","going_cold","next_follow_up_on","follow_up_overdue","booked_total","delivered_total","closed_at","created_at","updated_at"],"additionalProperties":false,"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand. As listed: getLead has its history."},"LeadTask":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","description":"What to do: \"Call about the 0% offer\"."},"status":{"type":"string","enum":["open","done","cancelled"],"description":"open: still owed. done: done (log what came of it on the lead). cancelled: no longer needed; set by the system when the lead closes. Done and cancelled are final."},"overdue":{"type":"boolean","description":"True for an open follow-up due before today at the lead's store."},"done_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was marked done. Set by the system."},"done_by_name":{"description":"Who marked it done, or null.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","due_on","note","status","overdue","done_at","done_by_name","created_at"],"additionalProperties":false,"description":"A follow-up a lead is owed on a day: \"call back Thursday about financing\", \"send the cover colors\". Its lead's owner owes it. Closing the lead cancels what is still open. schema.org: PlanAction."},"LeadActivity":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"kind":{"type":"string","enum":["call","text","email","visit","wet_test","note","inquiry","captured","assigned","stage","quote","won","lost"],"description":"What happened. Logged by people: call, text, email (each either way), visit (they came in, or were visited at home), wet_test (they soaked in a spa), note, inquiry (they asked again). Written by the system: captured, assigned, stage (moved by hand), quote (a quote was linked), won, lost. A call, text or email moves a new lead to contacted; a visit or wet test moves it to visited."},"occurred_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When it happened: now unless said otherwise, never in the future."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who did it or logged it. NULL for the system acting for nobody (a form)."},"staff_member_name":{"description":"Who did it or logged it, or null.","type":["string","null"]},"summary":{"type":"string","description":"What happened, in a sentence or two: \"Soaked in the Grandee and the Vanguard; liked the lounger. Wife wants to see the cover colors.\""}},"required":["id","kind","occurred_at","staff_member_id","staff_member_name","summary"],"additionalProperties":false,"description":"One thing that happened on a lead, as it was logged: a call, a text, a visit, a wet test, a note, or something the system records (captured, handed to someone, a quote linked, won, lost). Never changed: a correction is a new note. schema.org: Action."},"Lead":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"lead_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the business and assigned automatically."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is asking: a customer already, or a prospect (customer.is_prospect) made when they first asked. Fixed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"customer_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"is_prospect":{"type":"boolean","description":"True for someone who has asked about buying (a lead) and not yet bought. A prospect is a customer like any other (contact points, consent, sites, the counter's lookup) until their first order is agreed, when the system clears it and tells customer.created. Set only when the customer is first recorded."},"telephone":{"description":"Their primary (or any active) phone number, or null.","type":["string","null"]},"email":{"description":"Their primary (or any active) email address, or null.","type":["string","null"]},"lead_source_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Where the lead came from."},"lead_source_name":{"type":"string","description":"The name people see, unique within the business: \"Boise Home & Garden Show 2026\"."},"lead_source_kind":{"type":"string","enum":["walk_in","phone","web","show","referral","advertising","other"],"description":"What sort of source it is, so reports can add up across them: walk_in, phone, web, show (a home show or event), referral, advertising, other."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The showroom the lead belongs to: where they came in, or the store a form or call was for. Reports by store count it here."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The salesperson who owns it and owes its follow-ups. NULL while no one does, waiting for a manager to hand it out. Who it starts with is the business's choice (tenant_policy.lead_owner). Giving it to someone else needs lead.manage at its store."},"staff_member_name":{"description":"The owner's name, or null while no one owns it.","type":["string","null"]},"captured_by_staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who took it down. NULL for a lead from a form or another system."},"captured_by_staff_member_name":{"description":"Who took it down, or null.","type":["string","null"]},"interest":{"type":"string","description":"What they are after, in their words: \"6-person spa for the back deck, wants lounger, asking about 0% financing\"."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The model they are most interested in, when it is one the business sells."},"item_name":{"description":"That model's name, or null.","type":["string","null"]},"stage":{"type":"string","enum":["new","contacted","visited","quoted","won","lost"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages."},"lost_reason":{"anyOf":[{"type":"string","enum":["price","bought_elsewhere","financing","not_ready","no_response","other"]},{"type":"null"}],"description":"Why it was lost: price, bought_elsewhere (a competitor sold them one), financing (could not get it), not_ready (not this year), no_response (stopped answering), other (say what in lost_note). Set only for a lost lead."},"lost_note":{"description":"More on why it was lost: who they bought from, what price beat ours.","type":["string","null"]},"last_activity_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When anything was last logged on it. Set by the system. A lead quiet for tenant_policy.lead_stale_days is going cold."},"days_quiet":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Whole days since anything was last logged on it."},"going_cold":{"type":"boolean","description":"True for an open lead quiet for the business's lead_stale_days or more (getPolicy)."},"next_follow_up_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"When its earliest open follow-up is due, or null when none is set: a lead with no next step."},"follow_up_overdue":{"type":"boolean","description":"True when an open follow-up was due before today at its store."},"booked_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the orders linked to it that were agreed (open or completed) come to, tax included: the booked value it won. A decimal string with up to 2 decimal places, for example \"12.50\"."},"delivered_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Of those orders, what has been handed over, tax included: the delivered value. A decimal string with up to 2 decimal places, for example \"12.50\"."},"closed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was won or lost. Set by the system."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."},"contact_points":{"type":"array","items":{"type":"object","properties":{"contact_point_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The contact point."},"kind":{"type":"string","enum":["email","telephone"],"description":"email or telephone."},"value":{"type":"string","description":"The address or number itself: a lowercase email address, or a phone number in E.164 form such as +12085550100."},"marketing_consent":{"type":"string","enum":["unknown","granted","denied"],"description":"Whether promotional messages may be sent here: unknown, granted or denied. Send nothing promotional unless granted."},"service_consent":{"type":"string","enum":["unknown","granted","denied"],"description":"Whether messages about the customer's own orders, deliveries and service may be sent here: unknown, granted or denied."}},"required":["contact_point_id","kind","value","marketing_consent","service_consent"],"additionalProperties":false},"description":"Their active email addresses and phone numbers with what they agreed to on each. Nurture (the business's automations on lead.created) goes only where consent allows: marketing only where it is granted."},"will_hear_offers":{"type":"boolean","description":"True when at least one of their addresses has marketing consent granted, so a nurture message can reach them."},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/LeadTask"},"description":"Its open follow-ups, soonest first, then the last ten done or cancelled, newest first."},"activities":{"type":"array","items":{"$ref":"#/components/schemas/LeadActivity"},"description":"What happened on it, newest first: the last 50 entries."},"orders":{"type":"array","items":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The quote or order linked to it."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."},"order_date":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The business date of the order. Defaults to today at the location. It decides which tax rates apply, unless tax_rate_date is hand_over."},"opened_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The business date the customer agreed: the day a quote was opened, or the order date of an order written up open. A booked sale dates from here, a delivered one from its hand-overs. NULL while it is a quote, and for a quote cancelled unopened. Set by the system."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the customer pays in all: subtotal plus tax_total, plus fee_total and fee_tax_total. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."},"fulfilled_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"The part of total, tax included, for goods and services already handed over, plus the fees kept and any tax on them. Calculated. A decimal string with up to 2 decimal places, for example \"12.50\"."}},"required":["sales_order_id","order_number","status","order_date","opened_on","total","fulfilled_total"],"additionalProperties":false},"description":"The quotes and orders linked to it (sales_order.lead_id), oldest first."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","lead_number","customer_id","customer_name","customer_number","is_prospect","telephone","email","lead_source_id","lead_source_name","lead_source_kind","location_id","location_name","staff_member_id","staff_member_name","captured_by_staff_member_id","captured_by_staff_member_name","interest","item_id","item_name","stage","lost_reason","lost_note","last_activity_at","days_quiet","going_cold","next_follow_up_on","follow_up_overdue","booked_total","delivered_total","closed_at","created_at","updated_at","contact_points","will_hear_offers","tasks","activities","orders","next"],"additionalProperties":false,"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand."},"LeadPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LeadSummary"},"description":"Records on this page, open ones first by their next follow-up (none set first, then the most overdue), then closed ones newest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of leads."},"LeadCapture":{"type":"object","properties":{"lead":{"$ref":"#/components/schemas/Lead"},"customer_match":{"type":"string","enum":["customer_named","telephone","email","new_prospect"],"description":"How the person was found: the customer named, an existing customer with that phone number or email, or a new prospect recorded."},"repeat":{"type":"boolean","description":"True when they already had a lead open: this inquiry was logged on it (kind inquiry) and no new lead was made."},"contacts_not_added":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["email","telephone"],"description":"email or telephone."},"value":{"type":"string","description":"The address or number itself: a lowercase email address, or a phone number in E.164 form such as +12085550100."},"why":{"type":"string","description":"Why it was not put on the customer found."}},"required":["kind","value","why"],"additionalProperties":false},"description":"A phone number or email given that was not added to the customer found, because the name given does not match theirs. Check with the person, then addContactPoint."}},"required":["lead","customer_match","repeat","contacts_not_added"],"additionalProperties":false,"description":"A lead taken down: the lead, and how the person was found."},"LeadTaskListItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"lead_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The lead it follows up. Fixed."},"lead_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff quote, unique within the business and assigned automatically."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is asking: a customer already, or a prospect (customer.is_prospect) made when they first asked. Fixed."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"telephone":{"description":"Their primary (or any active) phone number, or null.","type":["string","null"]},"interest":{"type":"string","description":"What they are after, in their words: \"6-person spa for the back deck, wants lounger, asking about 0% financing\"."},"stage":{"type":"string","enum":["new","contacted","visited","quoted","won","lost"],"description":"Where it stands: new (not yet reached), contacted (spoken to), visited (came in, soaked in a wet test, or was visited at home), quoted (a quote is linked to it), won (an order linked to it was agreed; set by the system) or lost. What is logged moves it forward on its own (a call to contacted, a wet test to visited, a quote linked to quoted); a person can also move it among the open stages."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The salesperson who owns it and owes its follow-ups. NULL while no one does, waiting for a manager to hand it out. Who it starts with is the business's choice (tenant_policy.lead_owner). Giving it to someone else needs lead.manage at its store."},"staff_member_name":{"description":"The lead's owner, who owes it, or null.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The showroom the lead belongs to: where they came in, or the store a form or call was for. Reports by store count it here."},"location_name":{"type":"string","description":"Display name, for example \"Boise Showroom\"."},"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is due, at the lead's store. Moved to put it off."},"note":{"type":"string","description":"What to do: \"Call about the 0% offer\"."},"status":{"type":"string","enum":["open","done","cancelled"],"description":"open: still owed. done: done (log what came of it on the lead). cancelled: no longer needed; set by the system when the lead closes. Done and cancelled are final."},"overdue":{"type":"boolean","description":"True for an open follow-up due before today at the lead's store."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","lead_id","lead_number","customer_id","customer_name","telephone","interest","stage","staff_member_id","staff_member_name","location_id","location_name","due_on","note","status","overdue","created_at"],"additionalProperties":false,"description":"A follow-up a lead is owed on a day: \"call back Thursday about financing\", \"send the cover colors\". Its lead's owner owes it. Closing the lead cancels what is still open. schema.org: PlanAction. With its lead and customer."},"LeadTaskPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LeadTaskListItem"},"description":"Records on this page, soonest due first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of follow-ups."},"LeadReport":{"type":"object","properties":{"from":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The first day counted: leads taken down on or after it, at their store."},"to":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The last day counted."},"group_by":{"type":"string","enum":["source","salesperson","location"],"description":"What the rows are."},"whole_business":{"type":"boolean","description":"True when the key sees every lead (lead.manage everywhere). False: the rows count the leads at the stores where it holds lead.manage, and its own."},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The source, salesperson or store; null for leads no one owns."},"name":{"type":"string","description":"Its name: \"Nobody yet\" for leads no one owns."},"leads":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Leads taken down in the period."},"open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, still open."},"quoted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, those with a quote or order linked."},"won":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, won: an order linked was agreed."},"lost":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, lost."},"close_rate":{"description":"won / (won + lost), as a fraction to 4 places, \"0.2500\". Null while none has closed.","type":["string","null"]},"booked_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the agreed orders linked to them come to, tax included: booked value. A decimal string with up to 2 decimal places, for example \"12.50\"."},"delivered_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Of that, what has been handed over, tax included: delivered value. A decimal string with up to 2 decimal places, for example \"12.50\"."},"lost_reasons":{"type":"object","propertyNames":{"type":"string","enum":["price","bought_elsewhere","financing","not_ready","no_response","other"]},"additionalProperties":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"How many were lost for each reason, as {reason: count}, only the reasons that occur."}},"required":["id","name","leads","open","quoted","won","lost","close_rate","booked_total","delivered_total","lost_reasons"],"additionalProperties":false},"description":"One row per source, salesperson or store with leads in the period, most leads first."},"totals":{"type":"object","properties":{"leads":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Leads taken down in the period."},"open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, still open."},"quoted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, those with a quote or order linked."},"won":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, won: an order linked was agreed."},"lost":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Of them, lost."},"close_rate":{"description":"won / (won + lost), as a fraction to 4 places, \"0.2500\". Null while none has closed.","type":["string","null"]},"booked_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the agreed orders linked to them come to, tax included: booked value. A decimal string with up to 2 decimal places, for example \"12.50\"."},"delivered_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"Of that, what has been handed over, tax included: delivered value. A decimal string with up to 2 decimal places, for example \"12.50\"."},"lost_reasons":{"type":"object","propertyNames":{"type":"string","enum":["price","bought_elsewhere","financing","not_ready","no_response","other"]},"additionalProperties":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"How many were lost for each reason, as {reason: count}, only the reasons that occur."}},"required":["leads","open","quoted","won","lost","close_rate","booked_total","delivered_total","lost_reasons"],"additionalProperties":false,"description":"Every lead counted, added up: the groups add up to it."}},"required":["from","to","group_by","whole_business","groups","totals"],"additionalProperties":false,"description":"The pipeline by where leads came from, who works them or which store: of the leads taken down in a period, how many are still open, quoted, won and lost (and why), and what they booked and delivered."},"Layaway":{"type":"object","properties":{"sales_order_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The order on layaway: open, for a named customer, with nothing handed over when it was put on layaway."},"order_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The number staff and the customer quote, unique within the tenant and assigned automatically."},"status":{"type":"string","enum":["quote","open","completed","cancelled"],"description":"The order's status. quote: offered, not agreed. open: agreed, with goods still to hand over or money still to collect. completed: everything handed over and paid; set by the system, never sent. cancelled: will not happen; allowed only while nothing is with the customer and nothing is held for them beyond the business's cancellation fee, which it then keeps. Move quote to open when the customer agrees."},"state":{"type":"string","enum":["on_schedule","late","paid_up","completed","cancelled"],"description":"Where the layaway stands. on_schedule: paid up to what is due so far. late: an installment's day has passed without it paid. paid_up: paid in full, the goods ready to go home. completed: the goods went home. cancelled: the order was cancelled."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Who is buying. NULL for an anonymous counter sale, which must be paid in full when the goods are handed over."},"customer_name":{"type":"string","description":"The customer's name."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The location making the sale. Stock is handed over from here unless a fulfilment says otherwise."},"location_name":{"type":"string","description":"The store's name."},"total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the order comes to, tax included. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid_total":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What has been paid on it so far, less refunds. A decimal string with up to 2 decimal places, for example \"12.50\"."},"balance":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is still to pay before the goods go home: total less paid_total, never below 0. A decimal string with up to 2 decimal places, for example \"12.50\"."},"next_due_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"The day the next payment is due (or was, when late). Null once paid in full."},"next_due_amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What is still to pay of that installment. 0 once paid in full. A decimal string with up to 2 decimal places, for example \"12.50\"."},"late_amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What should have been paid by today and has not been. 0 when on schedule. A decimal string with up to 2 decimal places, for example \"12.50\"."},"days_late":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many days the oldest unpaid installment is past its day. 0 when on schedule."},"cancellation_fee":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"What the business keeps if the layaway is cancelled now, out of what was paid (tenant_policy.layaway_cancellation_fee_percent); the rest is refunded first. A decimal string with up to 2 decimal places, for example \"12.50\"."},"installments":{"type":"array","items":{"type":"object","properties":{"installment_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Which payment it is: 1 is the first (usually the down payment). Numbered from 1 with no gaps, in date order."},"due_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The day it is to be paid by, at the order's store. Each comes after the one before."},"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"How much is due by due_on, before what has been paid. The active installments add up to the order's total when the schedule is set; should the order's total change, the last one takes the difference. The last is what the order still comes to. A decimal string with up to 2 decimal places, for example \"12.50\"."},"paid":{"type":"string","pattern":"^-?\\d+(\\.\\d{1,2})?$","description":"How much of it has been paid: what was paid on the order counts against the installments in date order. A decimal string with up to 2 decimal places, for example \"12.50\"."},"status":{"type":"string","enum":["paid","due","late"],"description":"paid: paid in full. due: its day has not passed. late: its day has passed without it paid."}},"required":["installment_number","due_on","amount","paid","status"],"additionalProperties":false},"description":"The schedule, in date order."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["sales_order_id","order_number","status","state","customer_id","customer_name","location_id","location_name","total","paid_total","balance","next_due_on","next_due_amount","late_amount","days_late","cancellation_fee","installments","next"],"additionalProperties":false,"description":"An order on layaway: its schedule of payments and where it stands."},"LayawayPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Layaway"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of layaways."},"Treatment":{"type":"object","properties":{"sequence":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The order to do it in, starting at 1."},"parameter":{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","bromine","salt"],"description":"The reading that is out of range."},"reading":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What the reading was. A decimal string with up to 2 decimal places, for example \"12.50\"."},"target":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"What it should be brought to: the middle of its target range. A decimal string with up to 2 decimal places, for example \"12.50\"."},"item_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The product to sell: the smallest of the rule's packs that covers the dose, the largest when none does, or the rule's own item when it has no packs. NULL when the tenant has no dosing rule for this reading and direction."},"sku":{"description":"The product's SKU, so it can be sold.","type":["string","null"]},"item_name":{"description":"The product's name.","type":["string","null"]},"amount":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"How much to add. NULL when there is no rule or the volume of the water body is not recorded."},"amount_unit":{"description":"The unit of amount.","type":["string","null"]},"quantity":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many of item_id to sell to cover amount. NULL when the rule has no packs, or no amount could be worked out: sell one."},"pack_contents":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":" A decimal string with up to 3 decimal places, for example \"12.500\"."},{"type":"null"}],"description":"How much one of item_id holds, in amount_unit. NULL when the rule has no packs."},"instructions":{"description":"How to add it, or why no dose could be worked out.","type":["string","null"]}},"required":["sequence","parameter","reading","target","item_id","sku","item_name","amount","amount_unit","quantity","pack_contents","instructions"],"additionalProperties":false,"description":"One thing to do after a water test: a reading out of range, the product to add and how much. Derived: written by water_care.recommend() and replaced each time it runs. Do the treatments in sequence order; balancing alkalinity first makes the later ones hold."},"WaterTest":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"water_body_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The water tested."},"water_body_name":{"type":"string","description":"What to call it, for example \"Grandee hot tub\" or \"Back yard pool\"."},"tested_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the sample was tested: now unless given, and never more than a day ahead."},"location_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The location where it was tested, for a sample brought in."},"location_name":{"description":"The location's name, or null when the test was not taken at one.","type":["string","null"]},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who ran the test."},"staff_member_name":{"description":"The staff member's name, or null when none is recorded.","type":["string","null"]},"source":{"type":"string","enum":["manual","device"],"description":"manual: readings typed in from strips or a kit. device: readings sent by a testing device or its software."},"source_reference":{"description":"The device's or outside system's own id for the test, when source is device.","type":["string","null"]},"total_alkalinity":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Total alkalinity, in ppm."},"ph":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"pH, from 0 to 14."},"calcium_hardness":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Calcium hardness, in ppm."},"cyanuric_acid":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Cyanuric acid (stabilizer), in ppm."},"free_chlorine":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Free chlorine, in ppm."},"total_chlorine":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Total chlorine, in ppm. Total less free is combined chlorine, so it cannot be less than free_chlorine."},"bromine":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Bromine, in ppm."},"salt":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Salt, in ppm."},"notes":{"description":"What was seen or said: cloudy water, a new fill, a customer complaint.","type":["string","null"]},"treatments":{"type":"array","items":{"$ref":"#/components/schemas/Treatment"},"description":"What to add, in order. Empty when every reading taken is in range."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","water_body_id","water_body_name","tested_at","location_id","location_name","staff_member_id","staff_member_name","source","source_reference","total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","total_chlorine","bromine","salt","notes","treatments","created_at"],"additionalProperties":false,"description":"One test of a body of water: when, by whom, and what each reading was. A reading that was not taken is NULL. The tests of a water body in date order are its history."},"WaterTestPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WaterTest"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of water tests."},"WaterBody":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Whose water it is."},"customer_name":{"type":"string","description":"Name as shown on screens and documents: \"Pat Lee\" for a person, the trading name for an organization."},"site_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Where it is: one of the customer's own sites."},"equipment_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The equipment that holds it, when that is on record. It must be the customer's own."},"name":{"type":"string","description":"What to call it, for example \"Grandee hot tub\" or \"Back yard pool\"."},"kind":{"type":"string","enum":["hot_tub","swim_spa","pool","other"],"description":"hot_tub, swim_spa, pool or other. Target ranges can differ by kind."},"volume_gallons":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"How much water it holds, in US gallons. Doses scale with it; without it no dose can be worked out."},"sanitizer":{"anyOf":[{"type":"string","enum":["chlorine","bromine","salt","mineral","biguanide","other"]},{"type":"null"}],"description":"How the water is sanitized: chlorine, bromine, salt (a salt system making chlorine), mineral, biguanide or other."},"notes":{"description":"Free-form notes for staff, for example a recurring problem.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the customer no longer has it. Its test history stays."},"site_name":{"description":"The site's name, or null when the water body has no site.","type":["string","null"]},"equipment_name":{"description":"The equipment's name, or null when it is not a piece of equipment.","type":["string","null"]},"last_test":{"anyOf":[{"$ref":"#/components/schemas/WaterTest"},{"type":"null"}],"description":"The most recent test, with what it said to add."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","customer_id","customer_name","site_id","equipment_id","name","kind","volume_gallons","sanitizer","notes","is_active","site_name","equipment_name","last_test","created_at","updated_at"],"additionalProperties":false,"description":"One body of water a customer has: the water in a hot tub, a swim spa or a pool. Tests and their history belong to it. Usually it is the water in one piece of equipment."},"WaterBodyPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WaterBody"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of water bodies."},"TargetRange":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"parameter":{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","bromine","salt"],"description":"The reading the range is for: one of the reading columns of water_test."},"water_body_kind":{"anyOf":[{"type":"string","enum":["hot_tub","swim_spa","pool","other"]},{"type":"null"}],"description":"The kind of water body the range is for. NULL applies to every kind that has no range of its own."},"minimum":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The lowest acceptable reading. A decimal string with up to 2 decimal places, for example \"12.50\"."},"maximum":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"The highest acceptable reading. Treatments aim for the middle of the range. A decimal string with up to 2 decimal places, for example \"12.50\"."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","parameter","water_body_kind","minimum","maximum","updated_at"],"additionalProperties":false,"description":"Where one reading should sit. A reading outside its range gets a treatment. A tenant starts with common ranges and adjusts them to the product lines it sells."},"TargetRangePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TargetRange"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of target ranges."},"DosingPack":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The catalog item sold in this size, for example \"Chlorine granules, 5 lb\"."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"contents":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"How much product one of the item holds, in the rule's amount_unit: 80 for a 5 lb tub when the rule doses in oz. A decimal string with up to 3 decimal places, for example \"12.500\"."}},"required":["item_id","sku","item_name","contents"],"additionalProperties":false,"description":"One size a dosing rule's product is sold in, and how much of the dose it holds: a 1 lb tub holds 16 oz. A treatment sells the smallest active pack that covers its dose, or as many of the largest as it takes. A rule with no packs sells its own item, one of it. schema.org: Offer."},"DosingRule":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"parameter":{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","bromine","salt"],"description":"The reading the product moves."},"direction":{"type":"string","enum":["raise","lower"],"description":"raise or lower."},"item_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The product to add, from the catalog, so the treatment can be sold."},"amount":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$","description":"The amount of product the label states. A decimal string with up to 3 decimal places, for example \"12.500\"."},"amount_unit":{"type":"string","description":"The unit of amount, as the label gives it: oz, lb, tbsp, g."},"per_volume_gallons":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":"The volume of water that amount is for, in US gallons. A decimal string with up to 1 decimal places, for example \"12.5\"."},"changes_by":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":"How much that amount, in that volume, moves the reading. A decimal string with up to 2 decimal places, for example \"12.50\"."},"instructions":{"description":"How to add it, copied onto each treatment: \"Dissolve first. Run jets 20 minutes, retest.\"","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the rule is retired, for example when the product is discontinued."},"sku":{"type":"string","description":"The tenant's own stock code for the item, uppercase, unique within the tenant."},"item_name":{"type":"string","description":"Short name shown on screens, receipts and invoices."},"packs":{"type":"array","items":{"$ref":"#/components/schemas/DosingPack"},"description":"The sizes the product is sold in, smallest first. Empty sells the rule's own item."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","parameter","direction","item_id","amount","amount_unit","per_volume_gallons","changes_by","instructions","is_active","sku","item_name","packs","created_at","updated_at"],"additionalProperties":false,"description":"How the tenant moves one reading up or down: which product, and how far a given amount moves it in a given volume. Read off the product's label: \"1 oz per 500 gallons raises pH by 0.2\". One active rule per reading and direction."},"DosingRulePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DosingRule"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of dosing rules."},"LabReading":{"type":"object","properties":{"name":{"type":"string","description":"The lab's name for the test."},"value":{"type":"string","description":"The value as the lab printed it."},"unit":{"description":"The lab's unit, or null when it gave none.","type":["string","null"]},"reading":{"anyOf":[{"type":"string","enum":["total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","total_chlorine","bromine","salt"]},{"type":"null"}],"description":"The reading of ours it became, or null when it was not used."},"note":{"description":"Why it was not used, or how it was converted.","type":["string","null"]}},"required":["name","value","unit","reading","note"],"additionalProperties":false,"description":"One reading as the lab printed it, and what became of it."},"LabSample":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"lab_import_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The file or batch it came in."},"row_label":{"type":"string","description":"Where in what the lab sent it is: \"row 3\", \"sample 2\"."},"tested_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the lab tested it: when it came in, when the lab does not say."},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store whose lab it came from. Its samples' tests are recorded there."},"location_name":{"type":"string","description":"The store's name."},"lab_customer_reference":{"description":"The lab software's own id for the customer. Remembered when staff match the sample, so the next sample under it matches by itself.","type":["string","null"]},"lab_customer_name":{"description":"The customer's name as the lab has it. Shown to staff; never matched on, as names are not unique.","type":["string","null"]},"lab_telephone":{"description":"The customer's phone number as the lab has it, in E.164 form. Matched to a customer's number exactly.","type":["string","null"]},"lab_email":{"description":"The customer's email address as the lab has it, in lowercase. Matched to a customer's address exactly.","type":["string","null"]},"lab_water_label":{"description":"What the lab calls the water: \"Hot tub\", \"Back yard pool\". Picks between a customer's waters when it is one's name.","type":["string","null"]},"lab_water_kind":{"anyOf":[{"type":"string","enum":["hot_tub","swim_spa","pool","other"]},{"type":"null"}],"description":"The kind of water the lab says it is: hot_tub, swim_spa, pool or other. A customer's water of another kind is never matched to it."},"total_alkalinity":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Total alkalinity, in ppm."},"ph":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"pH, from 0 to 14."},"calcium_hardness":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Calcium hardness, in ppm."},"cyanuric_acid":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Cyanuric acid (stabilizer), in ppm."},"free_chlorine":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Free chlorine, in ppm."},"total_chlorine":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Total chlorine, in ppm."},"bromine":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$","description":" A decimal string with up to 2 decimal places, for example \"12.50\"."},{"type":"null"}],"description":"Bromine, in ppm."},"salt":{"anyOf":[{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,1})?$","description":" A decimal string with up to 1 decimal places, for example \"12.5\"."},{"type":"null"}],"description":"Salt, in ppm."},"lab_readings":{"type":"array","items":{"$ref":"#/components/schemas/LabReading"},"description":"Each reading as the lab printed it, {name, value, unit, reading, note}: reading is the one of ours it became, or null with a note saying why it was not used (a unit not understood, a value beyond the device's range, a test we do not keep)."},"status":{"type":"string","enum":["unmatched","recorded","discarded"],"description":"unmatched: waiting for staff to say whose water it is. recorded: a water test of a customer's water. discarded: thrown out, for discard_reason."},"customer_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Whose sample it is, once known. Can be known while it is still unmatched: the customer was found but which of their waters it is was not."},"customer_name":{"description":"The customer's name, once known.","type":["string","null"]},"customer_number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The customer's number, once known."},"matched_by":{"anyOf":[{"type":"string","enum":["lab_customer_reference","telephone","email","staff"]},{"type":"null"}],"description":"How the customer was found: lab_customer_reference (the lab's id, remembered from an earlier match), telephone, email, or staff."},"water_body_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"The water it was recorded against."},"water_body_name":{"description":"The water's name, once recorded.","type":["string","null"]},"water_test":{"anyOf":[{"$ref":"#/components/schemas/WaterTest"},{"type":"null"}],"description":"The water test it became, with what to add, once recorded; null before."},"discard_reason":{"description":"Why it was thrown out.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"next":{"type":"array","items":{"$ref":"#/components/schemas/NextAction"},"description":"Operations worth calling next from here, most useful first. Empty when there is nothing to suggest."}},"required":["id","lab_import_id","row_label","tested_at","location_id","location_name","lab_customer_reference","lab_customer_name","lab_telephone","lab_email","lab_water_label","lab_water_kind","total_alkalinity","ph","calcium_hardness","cyanuric_acid","free_chlorine","total_chlorine","bromine","salt","lab_readings","status","customer_id","customer_name","customer_number","matched_by","water_body_id","water_body_name","water_test","discard_reason","created_at","next"],"additionalProperties":false,"description":"One sample a water testing lab tested: who the lab says it is from, and its readings in our units. Matched to a customer's water by exact keys when it comes in, or by staff, it becomes a water test with what to add. Readings are copied as the lab sent them; correcting one is a new test."},"LabSamplePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LabSample"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of lab samples."},"LabImport":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"source":{"type":"string","description":"The adapter that read it, as a short lowercase key: generic_csv for a lab's spreadsheet export, or the integration's own key for results posted already read."},"file_name":{"description":"The name of the file uploaded, when there was one.","type":["string","null"]},"location_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The store whose lab it came from. Its samples' tests are recorded there."},"location_name":{"type":"string","description":"The store's name."},"staff_member_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},{"type":"null"}],"description":"Who brought it in."},"staff_member_name":{"description":"Who brought it in, or null for a key acting for nobody.","type":["string","null"]},"samples":{"type":"array","items":{"$ref":"#/components/schemas/LabSample"},"description":"The samples taken up from it, recorded or waiting for a match, in the order the lab sent them."},"duplicates":{"type":"array","items":{"type":"object","properties":{"row":{"type":"string","description":"Where in what was sent it is."},"lab_sample_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The sample already taken up, from an earlier import or this one."}},"required":["row","lab_sample_id"],"additionalProperties":false},"description":"Samples already taken up, so not added again: a file sent twice, or exports that overlap."},"rejected":{"type":"array","items":{"type":"object","properties":{"row":{"type":"string","description":"Where in what was sent it is."},"reason":{"type":"string","description":"Why it could not be taken up, and what to do."}},"required":["row","reason"],"additionalProperties":false},"description":"The rows that could not be read as a sample, each {row, reason}, so what the lab sent and what was kept can be reconciled."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."}},"required":["id","source","file_name","location_id","location_name","staff_member_id","staff_member_name","samples","duplicates","rejected","created_at"],"additionalProperties":false,"description":"One file or batch of readings a water testing lab sent: an export uploaded at the counter, or results an integration posted. Kept exactly as received, so a sample can be checked against what the lab said."},"Vendor":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Unique identifier (UUID v7, sortable by creation time)."},"name":{"type":"string","description":"The vendor's name, unique within the tenant."},"account_number":{"description":"The tenant's account number with this vendor, quoted on orders.","type":["string","null"]},"email":{"description":"Where orders and enquiries are sent, lowercase.","type":["string","null"]},"telephone":{"description":"Main phone number in E.164 form, for example +12085550100.","type":["string","null"]},"url":{"description":"Website or ordering portal.","type":["string","null"]},"street_address":{"description":"Street address. A second line (suite, unit) follows a line break.","type":["string","null"]},"address_locality":{"description":"City or town.","type":["string","null"]},"address_region":{"description":"State or province code.","type":["string","null"]},"postal_code":{"description":"ZIP or postal code.","type":["string","null"]},"address_country":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code."},"notes":{"description":"Free-form notes for staff: rep name, ordering quirks, freight terms.","type":["string","null"]},"is_active":{"type":"boolean","description":"False once the tenant stops buying from this vendor. Its history stays."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Fields this business has added to the record, as {key: value}."},"items_supplied":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many items this vendor currently supplies."},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record was created (UTC, ISO 8601)."},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the record last changed (UTC, ISO 8601)."}},"required":["id","name","account_number","email","telephone","url","street_address","address_locality","address_region","postal_code","address_country","notes","is_active","custom_fields","items_supplied","created_at","updated_at"],"additionalProperties":false,"description":"A business the tenant buys from: a manufacturer or a distributor. Purchase orders and item costs point at a vendor. schema.org: Organization."},"VendorPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Vendor"},"description":"Records on this page, oldest first."},"next_cursor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Pass as `cursor` to get the next page. Null when this is the last page: there is nothing more to fetch."}},"required":["data","next_cursor"],"additionalProperties":false,"description":"One page of vendors and the cursor for the next."},"ServiceIndex":{"type":"object","properties":{"name":{"type":"string","description":"Service name."},"version":{"type":"string","description":"API version."},"openapi_url":{"type":"string","description":"Path of the OpenAPI 3.1 document describing every operation."},"authentication":{"type":"string","description":"How to authenticate requests."},"start":{"type":"string","description":"The first call to make once authenticated: it says who you are and what you may do."}},"required":["name","version","openapi_url","authentication","start"],"additionalProperties":false,"description":"What this service is and where to start."},"Health":{"type":"object","properties":{"status":{"type":"string","const":"ok","description":"Always ok on a 200."}},"required":["status"],"additionalProperties":false,"description":"The API is up and can reach its database."}}},"paths":{"/":{"get":{"operationId":"getServiceIndex","summary":"Describe this service","tags":["Meta"],"description":"Entry point for a client that knows only the base URL. Points to the OpenAPI document.","security":[],"responses":{"200":{"description":"What this service is and where to start.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceIndex"}}}}}}},"/healthz":{"get":{"operationId":"getHealth","summary":"Check service health","tags":["Meta"],"description":"Returns 200 when the API can reach its database. Fails with 500 otherwise.","security":[],"responses":{"200":{"description":"The API is up and can reach its database.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/v1/identity":{"get":{"operationId":"getIdentity","summary":"Describe the caller","tags":["Identity"],"description":"Returns who this API key acts for, the business it belongs to, the current time on that business's clock, the modules it has, the permissions the key holds and how much off a sale it may give without order.discount. Call it first in a session, and again after a forbidden error to see what the key may do. On a counter computer it also names the device: its store, register and card reader.","responses":{"200":{"description":"Who is calling, for which business, at what time on its clock, and what it may do.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sign-in":{"post":{"operationId":"signIn","summary":"Sign in","tags":["Identity"],"description":"Signs a person in with their business, email and password, and returns a session key that acts for them, with its secret this once. Send it as the API key from then on. It stops working after the business's staff_session_hours (12 by default) on its clock, and the person signs in again. A person with no password yet sets one with the sign-in code a manager issues them (setPassword). Wrong passwords are counted: ten within fifteen minutes lock signing in for fifteen minutes, or until a manager lifts the lock (clearSignInLock). Too many refusals from one client or for one email in a short time are answered too_many_attempts without trying. Needs no API key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInInput"}}}},"security":[],"responses":{"201":{"description":"A person signed in: their session key, with its secret shown this once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"too_many_attempts: Too many refused sign-ins from this client, or for this person, in a short time. Do not retry now: `detail` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sign-in/password":{"post":{"operationId":"setPassword","summary":"Set a password with a sign-in code","tags":["Identity"],"description":"Sets a person's password with the one-time sign-in code a manager issued them (issueSignInCode), good for 24 hours, and signs them in as signIn does. The code is used up. Any session they had ends. Use it for a first password and for a forgotten one. Needs no API key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswordInput"}}}},"security":[],"responses":{"201":{"description":"A person signed in: their session key, with its secret shown this once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"too_many_attempts: Too many refused sign-ins from this client, or for this person, in a short time. Do not retry now: `detail` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sign-out":{"post":{"operationId":"signOut","summary":"Sign out","tags":["Identity"],"description":"Revokes the key that makes this request, at once and for good: a person's session when they sign out, or an agent's key it no longer needs. Returns the key as it now is.","responses":{"200":{"description":"A bearer credential for the HTTP API, held by a person signed in, an agent or an integration acting for one tenant. The application role has no direct access: it authenticates through app.authenticate_api_key() and lists keys through app.api_keys(). Who a key acts for, what it may do, and how keys are issued and revoked are in db/migrations/0002_staff.sql. The secret is never part of this record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/identity/password":{"post":{"operationId":"changePassword","summary":"Change your password","tags":["Identity"],"description":"Changes the password of the person signed in, given their current one. Only a session signed in to with the password may: not an agent, and not a session switched into with a PIN at a counter. Every other session of theirs ends; their agent keys go on working, so review those too.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordInput"}}}},"responses":{"200":{"description":"A password changed. Every other session of the person's has ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChanged"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/devices":{"post":{"operationId":"enrollDevice","summary":"Enroll a counter computer","tags":["Identity"],"description":"Enrolls a store's shared counter computer as a device and returns its device key's secret, this once. The device key acts for nobody and can only list who may switch in there (listDeviceStaff) and switch a person in with their PIN (switchStaff), which returns a short session acting for them. It names the register the counter takes cash into and the card reader it uses, each in use at that store, or none. It needs a person signed in with their password, which they enter again, holding device.manage at the store. It expires after expires_in_days (at most 365) and is revoked with revokeApiKey, which ends every session switched into on it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollDeviceInput"}}}},"responses":{"201":{"description":"A counter computer just enrolled: its device key, with its secret shown this once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrolledDevice"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/device/staff":{"get":{"operationId":"listDeviceStaff","summary":"List who may switch in on this counter","tags":["Identity"],"description":"For a counter's device key: the people who may switch in on it with their PIN, by name: active, with a PIN set, and holding a role at the device's store or everywhere. pin_locked says whose PIN is locked after five wrong tries, and pin_length how many digits it has, so a PIN pad can go as soon as it is typed. Ordered by name. Only a device key may call it.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."}],"responses":{"200":{"description":"One page of the people who may switch in on this counter, by name, and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceStaffPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/device/switch":{"post":{"operationId":"switchStaff","summary":"Switch a person in on this counter with their PIN","tags":["Identity"],"description":"For a counter's device key: switches a person in with their PIN and returns a session that acts for them, with its secret this once. It ends after the business's device_session_minutes (30 by default), when someone else switches in here (the counter's other session is signed out), or when the device is revoked. A wrong PIN is counted: five in a row, on any counter, lock it until the person signs in with their password or a manager clears it (clearPin). An unknown person, no PIN, a locked PIN and a wrong one are refused alike, and twenty refusals within the hour turn the counter away (too_many_attempts). With approval, a manager approves one action for whoever is at the counter, who stays switched in.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwitchStaffInput"}}}},"responses":{"201":{"description":"A person signed in: their session key, with its secret shown this once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"too_many_attempts: Too many refused sign-ins from this client, or for this person, in a short time. Do not retry now: `detail` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/identity/pin":{"post":{"operationId":"setPin","summary":"Set your PIN","tags":["Identity"],"description":"Sets or changes the PIN the person signed in switches in with on a store's counter computers (switchStaff), given their password again. Only a session signed in to with a password may: not one switched into with a PIN. A new PIN has the business's pin_length digits (6 by default), and is not one digit repeated or a run like 123456. Refused while the PIN is locked after wrong tries: signing in with the password lifts the lock.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPinInput"}}}},"responses":{"200":{"description":"A PIN set: the person can switch in with it on any counter device at a store they work at.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinSet"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/api-keys":{"get":{"operationId":"listApiKeys","summary":"List API keys","tags":["Identity"],"description":"Returns the business's API keys this key may see, oldest first, never with their secrets: every key with staff.manage for the whole business; with staff.manage at a location, those of the people based there; and always the keys that act for this key's own person, and this key itself. Sessions, agents' keys and integrations' account keys are all listed, each with what can be done with it next.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only keys that act for this person."},{"schema":{"type":"string","enum":["session","agent","account","device"]},"in":"query","name":"kind","required":false,"description":"Return only keys of this kind."},{"schema":{"type":"string","enum":["active","expired","revoked"]},"in":"query","name":"status","required":false,"description":"Return only keys with this status."}],"responses":{"200":{"description":"One page of API keys and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"issueApiKey","summary":"Issue an API key for an agent","tags":["Identity"],"description":"Issues an agent key and returns its secret, this once. acts_for me: the key acts for the person signed in (this must be their session key, and they enter their password again), holds what they hold, and what it does is theirs in the audit trail. acts_for nobody: the key acts for no person and holds only the roles given; it needs staff.manage for the whole business and every permission each role carries where it is given. A key that acts for someone else is never issued: only they can, and never from a session switched into with a PIN. read_only: the key only looks things up, every other request refused; a key for nobody with no roles must be. Every agent key expires, after expires_in_days (at most 365) on the business's clock.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueApiKeyInput"}}}},"responses":{"201":{"description":"A key just issued, with its secret, which is shown this once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssuedApiKey"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/api-keys/{api_key_id}":{"get":{"operationId":"getApiKey","summary":"Get an API key","tags":["Identity"],"description":"Returns one API key this key may see (as listApiKeys says), never with its secret.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"api_key_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A bearer credential for the HTTP API, held by a person signed in, an agent or an integration acting for one tenant. The application role has no direct access: it authenticates through app.authenticate_api_key() and lists keys through app.api_keys(). Who a key acts for, what it may do, and how keys are issued and revoked are in db/migrations/0002_staff.sql. The secret is never part of this record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/api-keys/{api_key_id}/rotate":{"post":{"operationId":"rotateApiKey","summary":"Rotate an agent's API key","tags":["Identity"],"description":"Replaces an agent key with a new secret, returned this once, keeping its name, person, roles, whether it only reads, and expiry. The old key keeps working for overlap_hours (at most 168), so whatever holds it can switch, then stops. A key that acts for a person is rotated only by that person's session, with their password entered again; one that acts for nobody by a key holding staff.manage for the whole business and what its roles carry. A key never rotates itself. Sessions end and are signed into again; integrations' account keys are never rotated here.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateApiKeyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"api_key_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A key rotated: the new key with its secret, shown this once, and the old one it replaces.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotatedApiKey"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/api-keys/{api_key_id}/revoke":{"post":{"operationId":"revokeApiKey","summary":"Revoke an API key","tags":["Identity"],"description":"Stops a key working at once and for good. Any key may revoke itself, and a person any key that acts for them. Revoking anyone else's needs staff.manage where its person is based (for the whole business for a key that acts for nobody) and every permission the key holds: never a key that holds more than this one. A counter's device key needs device.manage at its store, and revoking it ends every session switched into on it. Revoking a rotated key also revokes the key it replaced, if that is still working out its overlap. Revoking a key already revoked changes nothing. An integration's account key is never revoked here. Refused when it would leave nobody able to manage the business's staff.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"api_key_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A bearer credential for the HTTP API, held by a person signed in, an agent or an integration acting for one tenant. The application role has no direct access: it authenticates through app.authenticate_api_key() and lists keys through app.api_keys(). Who a key acts for, what it may do, and how keys are issued and revoked are in db/migrations/0002_staff.sql. The secret is never part of this record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-classes":{"get":{"operationId":"listTaxClasses","summary":"List tax classes","tags":["Tax"],"description":"Returns the groups items are taxed by, oldest first. One is the default for items that name none.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of tax classes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxClassPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createTaxClass","summary":"Add a tax class","tags":["Tax"],"description":"Adds a group of items taxed alike, such as repair labor a state does not tax. Give it rates in each jurisdiction with setTaxRate: a class with no rate in a jurisdiction is not taxed there. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaxClassInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A group of items taxed the same way, for example ordinary goods, or repair labor that a state does not tax. Rates are set per class. A new tenant has one class, Standard.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxClass"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-classes/{tax_class_id}":{"patch":{"operationId":"updateTaxClass","summary":"Update a tax class","tags":["Tax"],"description":"Changes the fields sent. Making a class the default takes the default from the one that had it. The default class cannot be retired: make another the default first. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaxClassInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tax_class_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A group of items taxed the same way, for example ordinary goods, or repair labor that a state does not tax. Rates are set per class. A new tenant has one class, Standard.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxClass"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-jurisdictions":{"get":{"operationId":"listTaxJurisdictions","summary":"List tax jurisdictions","tags":["Tax"],"description":"Returns the authorities the business collects tax for, oldest first, each with its rates by class.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of tax jurisdictions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxJurisdictionPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createTaxJurisdiction","summary":"Add a tax jurisdiction","tags":["Tax"],"description":"Adds an authority the business collects sales tax for, with its rates. Then put it in the tax areas where it applies. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaxJurisdictionInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"An authority the tenant collects sales tax for and reports to, for example \"State of Idaho\" or \"City of Hailey\". Tax is recorded per jurisdiction so each can be paid what it is owed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxJurisdiction"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-jurisdictions/{tax_jurisdiction_id}":{"patch":{"operationId":"updateTaxJurisdiction","summary":"Update a tax jurisdiction","tags":["Tax"],"description":"Renames a jurisdiction, or retires it with is_active false so it is no longer charged anywhere. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaxJurisdictionInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tax_jurisdiction_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"An authority the tenant collects sales tax for and reports to, for example \"State of Idaho\" or \"City of Hailey\". Tax is recorded per jurisdiction so each can be paid what it is owed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxJurisdiction"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-jurisdictions/{tax_jurisdiction_id}/rates":{"post":{"operationId":"setTaxRate","summary":"Set a tax rate from a date","tags":["Tax"],"description":"Sets what a jurisdiction charges on one class of item from a date on. A rate change is a new rate with a later effective_from, so orders dated before it keep the old rate. Sending the same class and date again corrects that rate, and re-taxes what is not yet handed over on quotes and open orders it applies to. Rate 0 stops taxing the class from that date. Returns the jurisdiction. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTaxRateInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tax_jurisdiction_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"An authority the tenant collects sales tax for and reports to, for example \"State of Idaho\" or \"City of Hailey\". Tax is recorded per jurisdiction so each can be paid what it is owed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxJurisdiction"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-areas":{"get":{"operationId":"listTaxAreas","summary":"List tax areas","tags":["Tax"],"description":"Returns the places the business taxes sales in, oldest first, each with its jurisdictions and the combined rate by class today.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of tax areas.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxAreaPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createTaxArea","summary":"Add a tax area","tags":["Tax"],"description":"Adds a place for tax purposes and the jurisdictions whose taxes apply there. Then name it on the locations and delivery sites in it (tax_area_id): a sale is taxed by its delivery site's area, or the selling location's. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaxAreaInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A place, for tax purposes: the set of jurisdictions whose taxes all apply there. \"Hailey\" is the State of Idaho plus the City of Hailey. Locations and delivery sites each name their area.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxArea"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-areas/{tax_area_id}":{"patch":{"operationId":"updateTaxArea","summary":"Update a tax area","tags":["Tax"],"description":"Changes the fields sent. tax_jurisdiction_ids replaces the area's jurisdictions. Quotes and open orders in the area are re-taxed on what is not yet handed over; what has been handed over keeps its tax. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaxAreaInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tax_area_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A place, for tax purposes: the set of jurisdictions whose taxes all apply there. \"Hailey\" is the State of Idaho plus the City of Hailey. Locations and delivery sites each name their area.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxArea"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/locations":{"get":{"operationId":"listLocations","summary":"List locations","tags":["Locations"],"description":"Returns the tenant's locations, oldest first: premises and the vehicles (service vans) that carry stock from them. Closed locations are included unless filtered out.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["premises","vehicle"]},"in":"query","name":"kind","required":false,"description":"Return only premises, or only vehicles."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only open (true) or only closed (false) locations."}],"responses":{"200":{"description":"One page of locations and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createLocation","summary":"Create a location","tags":["Locations"],"description":"Adds a location: premises, or a vehicle (kind vehicle) that carries stock from its home location, loaded and emptied with transferStock. Parts a technician uses on a job come off the van they drive (driver_staff_member_id). Fails with conflict when the branch_code is already used by another location. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLocationInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A physical place the tenant operates: showroom, warehouse or service base. Stock, registers, sales and staff hang off a location. schema.org: LocalBusiness with a PostalAddress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/locations/{location_id}":{"get":{"operationId":"getLocation","summary":"Get a location","tags":["Locations"],"description":"Returns one location by id.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"200":{"description":"A physical place the tenant operates: showroom, warehouse or service base. Stock, registers, sales and staff hang off a location. schema.org: LocalBusiness with a PostalAddress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateLocation","summary":"Update a location","tags":["Locations"],"description":"Changes the fields sent and leaves the rest untouched. Send null to clear an optional field. To close a location set is_active to false: locations are never deleted. Every field but its regular price list needs policy.manage, its name, address and phone too, as they are printed on receipts and statements; price_list_id needs price_list.manage. Closing or reopening it also takes away or gives back every role held there, so it needs staff.manage for the whole business as well.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLocationInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"200":{"description":"A physical place the tenant operates: showroom, warehouse or service base. Stock, registers, sales and staff hang off a location. schema.org: LocalBusiness with a PostalAddress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff":{"get":{"operationId":"listStaff","summary":"List staff","tags":["Staff"],"description":"Returns the people who work for the business, oldest first, each with the roles they hold. Former staff are included unless filtered out.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"home_location_id","required":false,"description":"Return only staff based at this location. Accepts the record's id or its branch code."}],"responses":{"200":{"description":"One page of staff members and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createStaffMember","summary":"Add a staff member","tags":["Staff"],"description":"Records a person who has started working for the business, and optionally gives them roles in the same step. Needs staff.manage for the whole business, or at the home location given; each role given needs what grantStaffRole needs.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStaffMemberInput"}}}},"responses":{"201":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}":{"get":{"operationId":"getStaffMember","summary":"Get a staff member","tags":["Staff"],"description":"Returns one staff member with their home location and the roles they hold now.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateStaffMember","summary":"Update a staff member","tags":["Staff"],"description":"Changes the fields sent. Set is_active to false when someone leaves: their API keys stop working for good and their history stays. Needs staff.manage for the whole business or at their home location (at both locations when moving them), and the key must itself hold every permission they hold to retire them, bring them back, or change the email they sign in with.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStaffMemberInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/roles":{"post":{"operationId":"grantStaffRole","summary":"Give a staff member a role","tags":["Staff"],"description":"Gives a person a role, everywhere or at one location. Giving a role they already hold there changes nothing. Needs staff.manage for the whole business or at that location, and the key must itself hold every permission the role carries there. Returns the staff member.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantStaffRoleInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/roles/{staff_role_id}/revoke":{"post":{"operationId":"revokeStaffRole","summary":"Take a role away from a staff member","tags":["Staff"],"description":"Takes away one role a person holds (a staff_role_id from their roles). The record that they held it stays. Needs staff.manage for the whole business or where the role is held, and the key must itself hold every permission the role carries there. Returns the staff member.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_role_id","required":true,"description":"The grant to take away, from the staff member's roles."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/sign-in-code":{"post":{"operationId":"issueSignInCode","summary":"Issue a sign-in code","tags":["Staff"],"description":"Issues a person a one-time sign-in code, shown this once and good for 24 hours, with which they set their password (setPassword): a first one, or one forgotten. Issuing another replaces it. Give it to them in person or by a channel only they read: whoever has it can sign in as them. So it needs staff.manage where they are based (for the whole business when they have no base) and every permission they hold, as retiring them does. The person must be active and have an email address, which is what they sign in with. It also lifts a sign-in lock, which would refuse the code.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"A one-time sign-in code, with which a person sets their password.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInCode"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/sign-in-lock/clear":{"post":{"operationId":"clearSignInLock","summary":"Lift a sign-in lock","tags":["Staff"],"description":"Lifts a person's sign-in lock (sign_in_locked_until) and forgets their wrong tries, so they can sign in at once with their password: for when someone else's guesses keep them locked out. Needs what issueSignInCode needs. Changes nothing when they are not locked. Returns the staff member.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/pin/unlock":{"post":{"operationId":"unlockPin","summary":"Unlock a person's PIN","tags":["Staff"],"description":"Lifts a person's PIN lock after five wrong tries and forgets the tries, keeping their PIN: for a PIN mistyped, not one someone else may know, which clearPin removes. Five more wrong tries lock it again. Needs what issueSignInCode needs, and a person signed in with their password, not switched in with a PIN. Changes nothing when it is not locked. Returns the staff member.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/pin/clear":{"post":{"operationId":"clearPin","summary":"Clear a person's PIN","tags":["Staff"],"description":"Removes a person's PIN, and its lock after five wrong tries, and signs out anyone switched in as them on a counter: for a PIN forgotten or told to someone (unlockPin keeps a PIN that was only mistyped). They set a new one (setPin) once signed in with their password. Needs what issueSignInCode needs, and a person signed in with their password, not switched in with a PIN. Changes nothing when they have no PIN. Returns the staff member.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A person who works for the tenant: salesperson, technician, delivery crew, counter or office staff. Sales, work orders and deliveries name the staff member who handled them. schema.org: Person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffMember"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/roles":{"get":{"operationId":"listRoles","summary":"List roles","tags":["Staff"],"description":"Returns the business's roles, oldest first, each with its permissions and how many people hold it. Retired roles are included unless filtered out.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of roles and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createRole","summary":"Create a role","tags":["Staff"],"description":"Defines a named set of permissions to give staff, and optionally how much off a sale its holders may give without order.discount (max_discount_percent). The key must itself hold every permission it puts in the role, and order.discount to give a discount allowance. Requires the staff.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoleInput"}}}},"x-required-permission":"staff.manage","responses":{"201":{"description":"A named set of permissions the tenant defines, for example \"Sales Manager\". Staff and API keys hold roles; what someone may do is the union of the permissions of the roles they hold. Anything no permission names, everyone may do.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/roles/{role_id}":{"get":{"operationId":"getRole","summary":"Get a role","tags":["Staff"],"description":"Returns one role with its permissions and how many people hold it.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"role_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A named set of permissions the tenant defines, for example \"Sales Manager\". Staff and API keys hold roles; what someone may do is the union of the permissions of the roles they hold. Anything no permission names, everyone may do.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateRole","summary":"Update a role","tags":["Staff"],"description":"Changes the fields sent. A change to permissions applies at once to everyone who holds the role. Set is_active to false to retire it: its holders lose its permissions. The key must itself hold every permission the change adds or removes, and order.discount to change max_discount_percent. Requires the staff.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"role_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"staff.manage","responses":{"200":{"description":"A named set of permissions the tenant defines, for example \"Sales Manager\". Staff and API keys hold roles; what someone may do is the union of the permissions of the roles they hold. Anything no permission names, everyone may do.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers":{"get":{"operationId":"listCustomers","summary":"Find customers","tags":["Customers"],"description":"Returns customers, oldest first, with their primary email and phone and the last day they bought. Use search to find one the way staff do at the counter: by name, customer number, email address or phone number (any format). The purchase filters find who has stopped coming, for example who bought chemicals this year but not in the last six weeks. getCustomer returns one whole, with sites and balance.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"last_purchased_before","required":false,"description":"Return only customers who have bought before, but not on or after this date: who has not been in lately."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"purchased_since","required":false,"description":"Return only customers who have bought on or after this date. With last_purchased_before, the regulars who have stopped coming."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"purchased_category_id","required":false,"description":"Count only purchases of items filed under this category or any below it, for last_purchased_before and purchased_since; alone, return customers who have ever bought from it."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"on_account","required":false,"description":"true: only house and contractor accounts, customers who buy on credit (payment_terms_days above 0 or a credit_limit), whatever they owe: who gets statements even when paid up. false: only those who pay on hand-over."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"exempt_without_certificate","required":false,"description":"true: only active customers marked tax-exempt who have no active tax exemption certificate at all, so no sale of theirs is covered: whom to ask for one (recordTaxExemptionCertificate). Those whose certificates are running out are listTaxExemptionCertificates with expiring_within_days."}],"responses":{"200":{"description":"One page of customers and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createCustomer","summary":"Add a customer","tags":["Customers"],"description":"Records a new customer, with their email addresses, phone numbers and sites in the same step. Search with listCustomers first: the same person is often already on file. Payment terms need credit.manage, a tax exemption tax.override, and a site's tax area site.tax_area. A customer starts on the business's credit limit and terms for new customers unless others are sent. Returns the customer whole.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerInput"}}}},"responses":{"201":{"description":"A person, household or business that buys from the tenant and is billed. The address here is the billing address; where goods and service go is a site. schema.org: Person or Organization, by kind.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}":{"get":{"operationId":"getCustomer","summary":"Get a customer whole","tags":["Customers"],"description":"Returns one customer whole: every contact point and what they consented to on each, every site, the equipment they own, their open quotes and orders, their latest trade-ins, what they have bought this year, and their balance: what they owe, how much is late, and what is held on deposit.","parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"A person, household or business that buys from the tenant and is billed. The address here is the billing address; where goods and service go is a site. schema.org: Person or Organization, by kind.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateCustomer","summary":"Update a customer","tags":["Customers"],"description":"Changes the fields sent: billing address, credit terms, tax exemption. Raising or removing the credit limit, lengthening the terms, or releasing an overdue hold until a later date needs credit.manage; a tax exemption needs tax.override. Contact points and sites have operations of their own. Set is_active to false for a customer the business no longer deals with.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"A person, household or business that buys from the tenant and is billed. The address here is the billing address; where goods and service go is a site. schema.org: Person or Organization, by kind.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/contact-points":{"post":{"operationId":"addContactPoint","summary":"Add an email address or phone number","tags":["Customers"],"description":"Records one more way to reach a customer, with what they agreed to receive on it. Phone numbers may be sent as people write them; ten digits are taken as North American. Making it primary makes the customer's other one of that kind not primary.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddContactPointInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"201":{"description":"One email address or phone number for a customer, with what the customer has agreed to receive on it. A household usually has several. Consent is recorded per address because the law attaches it to the number or mailbox, not the person; the audit trail holds when each consent changed and who changed it. schema.org: ContactPoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPoint"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/contact-points/{contact_point_id}":{"patch":{"operationId":"updateContactPoint","summary":"Update an email address or phone number","tags":["Customers"],"description":"Changes the fields sent. Record a change of consent here as soon as the customer gives or withdraws it, with consent_source saying how. The address itself cannot change: retire this one (is_active false) and add the new one, so consent given for one address never carries to another.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateContactPointInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"contact_point_id","required":true,"description":"The contact point, from the customer's contact_points."}],"responses":{"200":{"description":"One email address or phone number for a customer, with what the customer has agreed to receive on it. A household usually has several. Consent is recorded per address because the law attaches it to the number or mailbox, not the person; the audit trail holds when each consent changed and who changed it. schema.org: ContactPoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPoint"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sites":{"get":{"operationId":"listSites","summary":"Find sites","tags":["Customers"],"description":"Returns the places customers take delivery or have equipment, oldest first. Filter by customer, or search the address and name.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's sites. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of sites and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SitePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createSite","summary":"Add a site","tags":["Customers"],"description":"Records a place a customer takes delivery or has equipment: a home, a cabin, a rental. Deliveries and service visits go to a site, not to the billing address. A site's customer is fixed. Its tax area decides the tax on what is delivered there, so setting one needs site.tax_area.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSiteInput"}}}},"responses":{"201":{"description":"A place where a customer takes delivery or has equipment installed and serviced: a home, a cabin, a rental property. Deliveries, installed equipment and service visits point at a site, not at the customer's billing address. schema.org: Place with a PostalAddress and GeoCoordinates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sites/{site_id}":{"get":{"operationId":"getSite","summary":"Get a site","tags":["Customers"],"description":"Returns one site with its customer's name and the notes a crew needs to work there.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"site_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A place where a customer takes delivery or has equipment installed and serviced: a home, a cabin, a rental property. Deliveries, installed equipment and service visits point at a site, not at the customer's billing address. schema.org: Place with a PostalAddress and GeoCoordinates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateSite","summary":"Update a site","tags":["Customers"],"description":"Changes the fields sent. When the customer no longer has the place, set is_active to false; when it changes hands, retire it and add a site for the new owner. Changing its tax area needs site.tax_area.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSiteInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"site_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A place where a customer takes delivery or has equipment installed and serviced: a home, a cabin, a rental property. Deliveries, installed equipment and service visits point at a site, not at the customer's billing address. schema.org: Place with a PostalAddress and GeoCoordinates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-exemption-certificates":{"get":{"operationId":"listTaxExemptionCertificates","summary":"List tax exemption certificates, or the ones to chase","tags":["Customers"],"description":"Returns customers' tax exemption certificates, newest first. With expiring_within_days, returns instead the ones to chase, soonest first: active certificates of customers who still buy tax-exempt that expire within that many days or have already expired, and that no newer certificate of theirs renews. Each says how many days it has left and what to do next. A sale names the certificate it was made untaxed on (getSalesOrder), which is what an auditor asks for.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's certificates. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"integer","minimum":0,"maximum":365},"in":"query","name":"expiring_within_days","required":false,"description":"Return only the certificates to chase: expiring within this many days from today, or expired, and not renewed."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of tax exemption certificates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxExemptionCertificatePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/tax-exemption-certificates":{"post":{"operationId":"recordTaxExemptionCertificate","summary":"Record a customer's tax exemption certificate","tags":["Customers"],"description":"Records the certificate a customer gives to buy without sales tax: its number, why they are exempt, the state it is good in and the days it covers, and its scan (uploadFile first) if there is one. The customer then buys tax-exempt: each sale is untaxed on the certificate that covers its date and state, and names it. Their quotes and orders still being written up (nothing handed over or paid) that it covers are untaxed at once (open_orders); a sale already rung up is not. A customer who did not buy tax-exempt before needs tax.override to be given their first certificate; recording a renewal for one who does needs nothing more.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordTaxExemptionCertificateInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"201":{"description":"A certificate a customer gave the business to buy without sales tax: a resale certificate, a contractor's or an exempt organization's. A sale is untaxed on the one that covers its day and state, and names it. Never changed once recorded but for its scan and notes; one recorded in error is withdrawn while no sale names it. schema.org: Certification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxExemptionCertificate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tax-exemption-certificates/{tax_exemption_certificate_id}":{"get":{"operationId":"getTaxExemptionCertificate","summary":"Get a tax exemption certificate","tags":["Customers"],"description":"Returns one certificate: whose it is, what it says, whether it is in force today and how many days it has left, how many sales were made untaxed on it, and the quotes and orders it covers now.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tax_exemption_certificate_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A certificate a customer gave the business to buy without sales tax: a resale certificate, a contractor's or an exempt organization's. A sale is untaxed on the one that covers its day and state, and names it. Never changed once recorded but for its scan and notes; one recorded in error is withdrawn while no sale names it. schema.org: Certification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxExemptionCertificate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateTaxExemptionCertificate","summary":"Attach a certificate's scan, or withdraw it","tags":["Customers"],"description":"Changes the fields sent: the scan of the signed form (uploadFile first) and the notes. Send is_active false to withdraw a certificate recorded in error: refused once a sale made untaxed on it has had goods handed over or money taken, and the quotes and orders it covered are taxed again unless another certificate covers them. What a certificate says (number, reason, state, dates) never changes: record it again as it should be.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaxExemptionCertificateInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tax_exemption_certificate_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A certificate a customer gave the business to buy without sales tax: a resale certificate, a contractor's or an exempt organization's. A sale is untaxed on the one that covers its day and state, and names it. Never changed once recorded but for its scan and notes; one recorded in error is withdrawn while no sale names it. schema.org: Certification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxExemptionCertificate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/lead-sources":{"get":{"operationId":"listLeadSources","summary":"List lead sources","tags":["Leads"],"description":"Returns where the business's leads come from (walked in, phone call, website, a home show), by name. captureLead names one by its code.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of the business's lead sources.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadSourcePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createLeadSource","summary":"Add a lead source","tags":["Leads"],"description":"Adds a place leads come from, such as this year's home show or a newspaper ad, so the pipeline report counts it apart. Requires the lead.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLeadSourceInput"}}}},"x-required-permission":"lead.manage","responses":{"201":{"description":"Where the business's leads come from, as it names them: a walk-in, a phone call, the website's form, the spring home show. The pipeline report adds up by it, so a show worth repeating stands out. schema.org: Thing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadSource"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/lead-sources/{lead_source_id}":{"patch":{"operationId":"updateLeadSource","summary":"Change or retire a lead source","tags":["Leads"],"description":"Changes the fields sent. Retire a show once it is over (is_active false): its leads keep it. Requires the lead.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLeadSourceInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"lead_source_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its lead source code."}],"x-required-permission":"lead.manage","responses":{"200":{"description":"Where the business's leads come from, as it names them: a walk-in, a phone call, the website's form, the spring home show. The pipeline report adds up by it, so a show worth repeating stands out. schema.org: Thing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadSource"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/leads":{"post":{"operationId":"captureLead","summary":"Take down a lead","tags":["Leads"],"description":"Takes down someone asking about buying: at the counter, on the phone, from the website's form or a home show. Name a customer on file, or give a name with a phone number or email: someone already on file with that number or email is found (never merged), and anyone else is recorded as a prospect. Consent to hear offers is recorded as they gave it now, with who heard it; a refusal on file stands. Someone who already has a lead open is not given a second: this is logged on it as an inquiry (repeat true). The lead's owner and first follow-up are the business's choice (getPolicy) unless sent. Send idempotency_key from a form, so a second submit takes nothing down twice.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureLeadInput"}}}},"responses":{"200":{"description":"A lead taken down: the lead, and how the person was found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadCapture"}}}},"201":{"description":"A lead taken down: the lead, and how the person was found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadCapture"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listLeads","summary":"List leads","tags":["Leads"],"description":"Returns leads, newest first, each with its owner, stage, next follow-up and whether it is going cold. A key holding lead.manage at a store sees every lead there; any other key sees the leads its staff member owns or took down. Filter by owner (unassigned for those no one owns), store, source, stage, open or closed, or search by name, phone number or lead number. listLeadTasks has the follow-ups due.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only leads this salesperson owns."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"unassigned","required":false,"description":"Return only leads no one owns yet."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only leads at this store. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"code"},"in":"query","name":"lead_source_id","required":false,"description":"Return only leads from this source. Accepts the record's id or its lead source code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's leads. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["new","contacted","visited","quoted","won","lost"]},"in":"query","name":"stage","required":false,"description":"Return only leads at this stage."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"open","required":false,"description":"Return only open (true) or only closed (false) leads."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"going_cold","required":false,"description":"Return only open leads going cold."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."}],"responses":{"200":{"description":"One page of leads.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/leads/{lead_id}":{"get":{"operationId":"getLead","summary":"Get a lead whole","tags":["Leads"],"description":"Returns one lead: who is asking and how to reach them (with what they agreed to on each address), what they are after, its owner and stage, its follow-ups, what happened on it, the quotes and orders linked to it with what they booked and delivered, and what to do next. Any key may read the lead in front of it.","parameters":[{"schema":{"type":"string","x-natural-key":"lead_number"},"in":"path","name":"lead_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its lead number (as a number or a string of digits)."}],"responses":{"200":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateLead","summary":"Change a lead, or who owns it","tags":["Leads"],"description":"Changes the fields sent: what they are after, the model, the source, the store, the owner, or the stage among the open ones. Someone who works from its store may take a lead no one owns (staff_member_id: their own); giving it to someone else, taking it from its owner, or moving it to another store needs lead.manage at its store. A won or lost lead does not change. Losing it is markLeadLost; it is won by an order linked to it being agreed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLeadInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"lead_number"},"in":"path","name":"lead_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its lead number (as a number or a string of digits)."}],"responses":{"200":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/leads/{lead_id}/lost":{"post":{"operationId":"markLeadLost","summary":"Mark a lead lost, and why","tags":["Leads"],"description":"Closes an open lead as lost, with the reason the pipeline report counts: price, bought_elsewhere, financing, not_ready, no_response or other. Its open follow-ups are cancelled and the nurture messages still waiting are withdrawn. Refused while an order linked to it is agreed (it was won). Needs lead.manage unless it is your own lead. If they come back, take down a new lead; an order linked to this one and agreed after all still wins it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkLeadLostInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"lead_number"},"in":"path","name":"lead_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its lead number (as a number or a string of digits)."}],"responses":{"200":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/leads/{lead_id}/activities":{"post":{"operationId":"logLeadActivity","summary":"Log a call, visit or wet test on a lead","tags":["Leads"],"description":"Logs what happened: a call, text or email (moves a new lead to contacted), a visit or wet test (moves it to visited), a note, or an inquiry. Send follow_up to set the next step in the same request. A won or lost lead takes notes only. Send idempotency_key to make a retry safe.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogLeadActivityInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"lead_number"},"in":"path","name":"lead_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its lead number (as a number or a string of digits)."}],"responses":{"200":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"201":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/leads/{lead_id}/tasks":{"post":{"operationId":"addLeadTask","summary":"Add a follow-up to a lead","tags":["Leads"],"description":"Adds a follow-up due on a day (today or later), which the lead's owner owes. Returns the lead whole.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLeadTaskInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"lead_number"},"in":"path","name":"lead_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its lead number (as a number or a string of digits)."}],"responses":{"201":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/lead-tasks":{"get":{"operationId":"listLeadTasks","summary":"List follow-ups owed on leads","tags":["Leads"],"description":"Returns follow-ups, soonest due first, each with its lead, customer and phone number: \"what do I do today\" is mine=true with due_on_or_before today. Open ones unless status says otherwise. A key holding lead.manage at a store sees every lead there; any other key sees the leads its staff member owns or took down.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"mine","required":false,"description":"Return only follow-ups on leads this key's staff member owns."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only follow-ups on leads this salesperson owns."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only follow-ups on leads at this store. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"due_on_or_before","required":false,"description":"Return only follow-ups due on or before this day."},{"schema":{"default":"open","type":"string","enum":["open","done","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only follow-ups in this status."}],"responses":{"200":{"description":"One page of follow-ups.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadTaskPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/lead-tasks/{lead_task_id}/complete":{"post":{"operationId":"completeLeadTask","summary":"Mark a follow-up done","tags":["Leads"],"description":"Marks a follow-up done, logs what came of it (activity) and sets the next one (follow_up), in one request. Sent again for a follow-up already done, it changes nothing and returns the lead. Returns the lead whole.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteLeadTaskInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"lead_task_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/lead-tasks/{lead_task_id}":{"patch":{"operationId":"updateLeadTask","summary":"Put off a follow-up, or reword it","tags":["Leads"],"description":"Changes an open follow-up's day (today or later) or what it says. Done and cancelled ones are final. Returns the lead whole.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLeadTaskInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"lead_task_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"Someone's pursuit of a purchase, from the day they ask to the day they agree to buy or walk away: one per customer at a time. Won when an order linked to it is agreed, never by a person saying so; lost with a reason. schema.org: Demand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/leads":{"get":{"operationId":"getLeadReport","summary":"The lead pipeline by source, salesperson or store","tags":["Leads"],"description":"Of the leads taken down in a period (from and to, at each lead's store; the last 90 days by default), how many are open, quoted, won and lost (and why), the close rate, and what they booked (agreed orders linked to them) and delivered, grouped by source, salesperson or store. Counted by when the lead came in, so a month's close rate is not flattered by leaving out what is still open. A key holding lead.manage at a store sees every lead there; any other key sees the leads its staff member owns or took down.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"The first day: leads taken down on or after it. Default 90 days ago."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"The last day. Default today."},{"schema":{"default":"source","type":"string","enum":["source","salesperson","location"]},"in":"query","name":"group_by","required":false,"description":"What each row is: source (the default), salesperson (the owner) or location (the store)."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Count only leads at this store. Accepts the record's id or its branch code."}],"responses":{"200":{"description":"The pipeline by where leads came from, who works them or which store: of the leads taken down in a period, how many are still open, quoted, won and lost (and why), and what they booked and delivered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadReport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/vendors":{"get":{"operationId":"listVendors","summary":"List vendors","tags":["Purchasing"],"description":"Returns the businesses this one buys from, oldest first. Search by name or account number. Vendors no longer bought from are included unless filtered out.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of vendors and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createVendor","summary":"Add a vendor","tags":["Purchasing"],"description":"Records a manufacturer or distributor the business buys from. Names are unique. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVendorInput"}}}},"x-required-permission":"catalog.manage","responses":{"201":{"description":"A business the tenant buys from: a manufacturer or a distributor. Purchase orders and item costs point at a vendor. schema.org: Organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vendor"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/vendors/{vendor_id}":{"get":{"operationId":"getVendor","summary":"Get a vendor","tags":["Purchasing"],"description":"Returns one vendor with how to reach them and how many items they supply.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"vendor_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A business the tenant buys from: a manufacturer or a distributor. Purchase orders and item costs point at a vendor. schema.org: Organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vendor"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateVendor","summary":"Update a vendor","tags":["Purchasing"],"description":"Changes the fields sent. Set is_active to false once the business stops buying from them; their history stays. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVendorInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"vendor_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"A business the tenant buys from: a manufacturer or a distributor. Purchase orders and item costs point at a vendor. schema.org: Organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vendor"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/brands":{"get":{"operationId":"listBrands","summary":"List brands","tags":["Catalog"],"description":"Returns the brands the business carries, oldest first. Search by name.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of brands and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createBrand","summary":"Add a brand","tags":["Catalog"],"description":"Records a brand the business carries. Names are unique. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrandInput"}}}},"x-required-permission":"catalog.manage","responses":{"201":{"description":"The name a product is sold under, for example Hot Spring or BioGuard. A brand is what the shopper recognizes; who the tenant buys it from is a vendor. schema.org: Brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Brand"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/brands/{brand_id}":{"patch":{"operationId":"updateBrand","summary":"Update a brand","tags":["Catalog"],"description":"Renames a brand, or retires it with is_active false. Its items keep it. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBrandInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"brand_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"The name a product is sold under, for example Hot Spring or BioGuard. A brand is what the shopper recognizes; who the tenant buys it from is a vendor. schema.org: Brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Brand"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/categories":{"get":{"operationId":"listCategories","summary":"List categories","tags":["Catalog"],"description":"Returns the categories items are filed under, oldest first, each with its path from the top of the tree. Walk the tree with top_level, then parent_category_id.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"parent_category_id","required":false,"description":"Return only the categories directly under this one."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"top_level","required":false,"description":"Send true to return only the top-level categories."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of categories and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createCategory","summary":"Add a category","tags":["Catalog"],"description":"Adds a category, at the top of the tree or under another. Names are unique among siblings. It can name the income and cost of goods accounts its items' sales post to, so margin is read per product line; naming them needs ledger.post as well. It can set its own cancellation and restocking fees and how long its goods can come back (returnable_days), which needs policy.manage as well. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryInput"}}}},"x-required-permission":"catalog.manage","responses":{"201":{"description":"A node in the tree items are filed under, for example Water Care, then Sanitizers. Reports group sales by it and a web store browses by it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/categories/{category_id}":{"patch":{"operationId":"updateCategory","summary":"Update a category","tags":["Catalog"],"description":"Renames a category, moves it under another (taking its subcategories along), retires it, or points its items' sales at other income and cost of goods accounts (which needs ledger.post as well, and applies to sales from then on), or changes its cancellation and restocking fees or how long its goods can come back (which needs policy.manage as well, and applies to cancellations and returns from then on). Items filed under it keep it. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"category_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"A node in the tree items are filed under, for example Water Care, then Sanitizers. Reports group sales by it and a web store browses by it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/items":{"get":{"operationId":"listItems","summary":"Find items","tags":["Catalog"],"description":"Returns items, oldest first, with their price and how many are on hand in all. Search the way staff do: by SKU, name, model, manufacturer part number, barcode, or the vendor's own code. getItem adds stock by location and who supplies it.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string"},"in":"query","name":"sku","required":false,"description":"Return only the item with exactly this SKU. Case does not matter."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"brand_id","required":false,"description":"Return only items of this brand."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"category_id","required":false,"description":"Return only items filed under this category or any category below it."},{"schema":{"type":"string","enum":["good","service"]},"in":"query","name":"kind","required":false,"description":"Return only goods or only services."},{"schema":{"type":"string","enum":["none","quantity","serial"]},"in":"query","name":"tracking","required":false,"description":"Return only items whose stock is kept this way."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of items and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ItemPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createItem","summary":"Add an item","tags":["Catalog"],"description":"Adds something the business sells: a good or a service. Say how its stock is kept (tracking) with care: it is fixed once the item exists. Who supplies it, and at what cost, can be recorded in the same step. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateItemInput"}}}},"x-required-permission":"catalog.manage","responses":{"201":{"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/items/{item_id}":{"get":{"operationId":"getItem","summary":"Get an item","tags":["Catalog"],"description":"Returns one item with its stock at each location and who supplies it. Vendor costs are shown only to a key that holds cost.view.","parameters":[{"schema":{"type":"string","x-natural-key":"sku"},"in":"path","name":"item_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its SKU."}],"responses":{"200":{"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateItem","summary":"Update an item","tags":["Catalog"],"description":"Changes the fields sent: price, name, brand, category. A new price applies to orders priced from now on. Moving the item to a category whose sales post to other accounts needs ledger.post as well, and to one with other fees or return window policy.manage. kind and tracking are fixed. Set is_active to false to retire the item from sale. Set lot_tracked to record the lot of everything of the item that moves, for recalls: stock on hand goes into its unknown lot. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateItemInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"sku"},"in":"path","name":"item_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its SKU."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/items/{item_id}/vendors":{"post":{"operationId":"setItemVendor","summary":"Record who supplies an item and at what cost","tags":["Catalog"],"description":"Records one vendor's offer of an item, or changes the fields sent on the one already recorded: their code for it, its cost, whether to order from them by default. Making one preferred makes the item's other vendors not preferred. Returns the item. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetItemVendorInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"sku"},"in":"path","name":"item_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its SKU."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/items/{item_id}/option-groups":{"post":{"operationId":"setItemOptionGroup","summary":"Record a choice a model is built with, and its options","tags":["Catalog"],"description":"Records one option group of a serial-tracked model built to order (Shell color, Cabinet, Jet package) with its options and what each adds to the price and to the factory's cost, or changes the fields sent on the group and options of those names. A required group must be answered whenever the model is ordered from the factory or sold with options. Set is_active to false to stop offering a group or an option: units already built with it keep it. Returns the item. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetItemOptionGroupInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"sku"},"in":"path","name":"item_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its SKU."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/items/{item_id}/packs":{"post":{"operationId":"setItemPack","summary":"Record a pack an item is sold in","tags":["Catalog"],"description":"Records a pack the item is also sold in beside its own unit, such as a case of 6 bottles at a case price, or changes the fields sent on the pack of that name. Selling a pack draws pack_size of the item's own stock. Set is_active to false to stop selling it. Returns the item. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetItemPackInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"sku"},"in":"path","name":"item_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its SKU."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"One thing the tenant sells, as it appears on a price tag or an invoice line: a physical good, or a service such as labor or a delivery fee. An item is the definition; how many are on the shelf, and each individual serialized unit, live in inventory. schema.org: Product (a good) or Service, with price as its Offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-channels":{"get":{"operationId":"listSalesChannels","summary":"List sales channels","tags":["Catalog"],"description":"Returns the ways orders reach the business (the counter, the web store, the phone), each with how many items it offers.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of sales channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesChannelPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createSalesChannel","summary":"Add a sales channel","tags":["Catalog"],"description":"Adds a way orders reach the business, and the location whose stock it offers. Then list items on it. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSalesChannelInput"}}}},"x-required-permission":"catalog.manage","responses":{"201":{"description":"A way orders reach the tenant: the counter, the web store, the phone, a technician in the field. Orders name their channel, so sales can be reported by it, and a web channel says which items it offers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesChannel"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-channels/{sales_channel_id}":{"patch":{"operationId":"updateSalesChannel","summary":"Update a sales channel","tags":["Catalog"],"description":"Changes the fields sent, or closes the channel with is_active false. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesChannelInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sales_channel_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"A way orders reach the tenant: the counter, the web store, the phone, a technician in the field. Orders name their channel, so sales can be reported by it, and a web channel says which items it offers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesChannel"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-channels/{sales_channel_id}/listings":{"get":{"operationId":"listChannelListings","summary":"List what a channel offers","tags":["Catalog"],"description":"Returns the items listed on one channel, each with the price it shows there.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_listed","required":false,"description":"Return only listed (true) or only withdrawn (false) items."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sales_channel_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One page of listings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelListingPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"setChannelListing","summary":"Offer an item on a channel","tags":["Catalog"],"description":"Lists an item on a channel, at its own price there or the item's, or withdraws it with is_listed false. Sending it again for the same item changes the listing. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetChannelListingInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sales_channel_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"One item offered on one sales channel, such as a product in the web store. The outside system's id for the listing is an external_link on this row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelListing"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/price-lists":{"get":{"operationId":"listPriceLists","summary":"List price lists","tags":["Price Lists"],"description":"Returns the business's price lists, oldest first: regular lists (a store's or region's own prices) and offer lists (trade, employee and contract prices, and sales), each with the stores it applies at.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["regular","offer"]},"in":"query","name":"kind","required":false,"description":"Return only regular, or only offer, lists."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of price lists and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceListPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createPriceList","summary":"Start a price list","tags":["Price Lists"],"description":"Creates a regular list (a store's or region's own prices: give it to stores with updateLocation, price_list_id) or an offer list that competes with the regular price, the lowest winning: for everyone (a sale), or only for customers put on it (audience assigned: trade, employee, contract), at every store or only those in stores. Then put prices on it with setPriceListPrices. Needs price_list.manage. Requires the price_list.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePriceListInput"}}}},"x-required-permission":"price_list.manage","responses":{"201":{"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceList"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/price-lists/{price_list_id}":{"get":{"operationId":"getPriceList","summary":"Get a price list with its prices","tags":["Price Lists"],"description":"Returns one price list with the stores it applies at and its prices in force on a date (today unless on is sent) or starting after it.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"on","required":false,"description":"Read the entries in force on this order date, and those starting after it. Defaults to today."},{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"price_list_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its price list code."}],"responses":{"200":{"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceList"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updatePriceList","summary":"Rename, retire or rescope a price list","tags":["Price Lists"],"description":"Changes the fields sent. is_active false retires the list: it prices no new line, and lines already priced from it keep their prices. stores replaces the stores an offer list is limited to. A list's kind is fixed. Needs price_list.manage. Requires the price_list.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePriceListInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"price_list_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its price list code."}],"x-required-permission":"price_list.manage","responses":{"200":{"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceList"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/price-lists/{price_list_id}/prices":{"post":{"operationId":"setPriceListPrices","summary":"Put prices on a price list","tags":["Price Lists"],"description":"Puts prices on the list from a date (today unless valid_from is sent): a fixed price for an item or one of its packs, or, on an offer list, a percent off or a price for every item filed directly under a category; each from a quantity on one line (min_quantity). A price already in force for the same item, pack or category and quantity is ended the day before, so orders already written keep theirs. Sending the same prices again changes nothing. Needs price_list.manage. Requires the price_list.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPriceListPricesInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"price_list_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its price list code."}],"x-required-permission":"price_list.manage","responses":{"200":{"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceList"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/price-lists/{price_list_id}/prices/end":{"post":{"operationId":"endPriceListPrices","summary":"End prices on a price list","tags":["Price Lists"],"description":"Ends the entries named on a date: orders dated after it no longer take them. Lines already priced from them keep their prices. Needs price_list.manage. Requires the price_list.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndPriceListPricesInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"price_list_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its price list code."}],"x-required-permission":"price_list.manage","responses":{"200":{"description":"A set of prices. A regular list is a store's or a region's own prices: the stores that name it in location.price_list_id sell at them instead of the items' own. An offer list competes with the regular price, and the lowest price wins: a trade or contractor price, an employee price or a contract (audience assigned, for the customers put on it), or a sale (audience everyone, dated by its entries). schema.org: OfferCatalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceList"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/price-lists":{"get":{"operationId":"getCustomerPriceLists","summary":"Get the price lists a customer is on","tags":["Price Lists"],"description":"Returns the offer lists a customer has been put on (trade, employee, contract), newest first, with whether each prices their orders today.","parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"The offer lists a customer is put on (a trade, employee or contract price), which compete with the store's regular prices on their orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPriceLists"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"assignCustomerPriceList","summary":"Put a customer on a price list","tags":["Price Lists"],"description":"Gives a customer an offer list that is only for those assigned to it (a trade or contractor price, an employee price, a contract) from a date, today unless sent. Their orders dated from then take its price wherever it is lower. Needs price_list.manage: it is a price concession. Requires the price_list.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignCustomerPriceListInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"x-required-permission":"price_list.manage","responses":{"200":{"description":"The offer lists a customer is put on (a trade, employee or contract price), which compete with the store's regular prices on their orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPriceLists"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/price-lists/{assignment_id}/end":{"post":{"operationId":"endCustomerPriceList","summary":"Take a customer off a price list","tags":["Price Lists"],"description":"Ends a customer's place on a list: valid_through is the last order date it prices, and orders dated after it no longer take its prices. Without valid_through it ends at once: yesterday is its last day, so orders dated today no longer take it (a place begun today never applies). Orders already written keep their prices until repriced (repriceSalesOrder). Needs price_list.manage. Requires the price_list.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndCustomerPriceListInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assignment_id","required":true,"description":"The customer's place on the list, as getCustomerPriceLists shows it (id)."}],"x-required-permission":"price_list.manage","responses":{"200":{"description":"The offer lists a customer is put on (a trade, employee or contract price), which compete with the store's regular prices on their orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPriceLists"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/item-prices":{"get":{"operationId":"getItemPrices","summary":"See what an item would cost a customer at a store","tags":["Price Lists"],"description":"Answers what one line of an item would cost: at a store, for a customer (or none), on a date, for a quantity. Returns every price that could apply (the item's or pack's own, the store's regular list, each offer list the customer gets there) with why, and the one an order line would take: the lowest, a tie going to the regular price. Options a model is built with are added on top.","parameters":[{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":true,"description":"The item. Accepts its id or its SKU. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":true,"description":"The store the sale is made at. Accepts its id or its branch code. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Who is buying. Leave out for a sale with no customer. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,3})?$"},"in":"query","name":"quantity","required":false,"description":"How many on one line, in packs when pack_text is sent. Defaults to 1. A decimal string with up to 3 decimal places, for example \"12.500\"."},{"schema":{"type":"string","minLength":1,"maxLength":50},"in":"query","name":"pack_text","required":false,"description":"Price one of the item's packs."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"on","required":false,"description":"The order date. Defaults to today at the store."}],"responses":{"200":{"description":"What one line of an item would cost at a store, for a customer, on a date: every price that could apply and the one a sales order line would take.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ItemPrices"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/reprice":{"post":{"operationId":"repriceSalesOrder","summary":"Bring a quote or order to today's prices","tags":["Price Lists"],"description":"Prices every line nothing of which has been handed over again from the items and price lists as they are now: a quote is first dated today, so an expired quote is brought to today's prices (and tax rates); an open order keeps its date. A line at its list price takes the new one; a line priced by hand keeps its price unless include_hand_priced, which needs order.discount. A hand price now further below its list price is weighed against the discount allowance. Set a new expires_on with updateSalesOrder before offering an expired quote again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepriceSalesOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-adjustment-reasons":{"get":{"operationId":"listStockAdjustmentReasons","summary":"List the reasons stock is lost or found","tags":["Inventory"],"description":"Returns the business's reasons for stock lost, damaged or found (broken, expired, theft, found, ...), oldest first, each with its code and the account its cost posts to. adjustStock, writeDownSerializedUnit and postStockCount name one by its code or id; getStockShrink reads losses by them.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of reasons for stock lost or found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockAdjustmentReasonPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createStockAdjustmentReason","summary":"Add a reason stock is lost or found","tags":["Inventory"],"description":"Adds one of the business's reasons for stock lost, damaged or found, with a code to name it by. ledger_account_id sends its cost to an expense account of the business's own (Store Supplies for goods used in the showroom); left out, it posts to Inventory Adjustments. Needs policy.manage. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStockAdjustmentReasonInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"Why stock was lost, damaged or found, as the business names it: broken, expired, theft, used in the showroom, damaged in freight, found. An adjustment, a write-down and a stock count's differences each carry one, so losses can be read by cause and store. A new business starts with a few, which it may rename, retire or add to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockAdjustmentReason"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-adjustment-reasons/{adjustment_reason_id}":{"patch":{"operationId":"updateStockAdjustmentReason","summary":"Change or retire a reason stock is lost or found","tags":["Inventory"],"description":"Renames a reason, points its cost at another account, says whether a note is required, makes it the reason a count's differences are put down to, or retires it (is_active false). Its code is fixed. Stock already moved for it stays posted where it was. Needs policy.manage. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStockAdjustmentReasonInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"adjustment_reason_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its reason code."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"Why stock was lost, damaged or found, as the business names it: broken, expired, theft, used in the showroom, damaged in freight, found. An adjustment, a write-down and a stock count's differences each carry one, so losses can be read by cause and store. A new business starts with a few, which it may rename, retire or add to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockAdjustmentReason"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock":{"get":{"operationId":"listStock","summary":"See what is on hand","tags":["Inventory"],"description":"Returns how much of each item is on hand at each location, with its value and average cost for a key that holds cost.view. Filter by item or location, or search SKUs and names. Items that have never moved at a location have no row there.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only this item's stock. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only stock at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"on_hand","required":false,"description":"Send true to leave out rows with nothing on hand."}],"responses":{"200":{"description":"One page of stock levels and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLevelPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/movements":{"get":{"operationId":"listStockMovements","summary":"Read the stock ledger","tags":["Inventory"],"description":"Returns stock movements, oldest first (newest_first=true for the latest first): every change in stock and why. A sale or return names its order and customer, a receipt its purchase order and vendor, and a movement of an item tracked by lot the lots it moved. Filter by item, location, unit, lot or kind: with a lot and kind=receipt, it says who supplied a lot and when. Costs are shown to a key that holds cost.view. A sale or return names its order and customer only to a key that may see that sale (sales.view at the location, or credit on the order). listSales reads sales and returns with their revenue, newest first, and who bought a lot.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only this item's movements. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only movements at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"serialized_unit_id","required":false,"description":"Return only this unit's movements: its history."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"stock_lot_id","required":false,"description":"Return only movements that took or brought this lot: where it came from and went."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"lot_code","required":false,"description":"Return only movements of a lot with this code, of any item; add item_id for one item's."},{"schema":{"type":"string","enum":["opening","adjustment","sale","sale_return","receipt","vendor_return","transfer_out","transfer_in","count","revaluation","landed_cost"]},"in":"query","name":"kind","required":false,"description":"Return only movements of this kind."},{"schema":{"type":"string","x-natural-key":"code"},"in":"query","name":"adjustment_reason_id","required":false,"description":"Return only stock lost or found for this reason, by its code or id. Accepts the record's id or its reason code."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"newest_first","required":false,"description":"Send true to page from the latest movement back; the cursor then moves back in time."}],"responses":{"200":{"description":"One page of the stock ledger and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockMovementPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/adjustments":{"post":{"operationId":"adjustStock","summary":"Put opening stock on hand, or correct stock","tags":["Inventory"],"description":"Writes stock movements by hand: opening stock when the business starts using the system, or a correction for damage, loss or a find, with its reason (adjustment_reason_id, by code). Stock coming in takes unit_cost or extended_cost (neither uses the average cost); stock going out is costed by the system. A serialized unit moves one at a time, at its own cost: create it first with createSerializedUnit. For an item tracked by lot, name the lot (lot_code, and expires_on for a new one); moving stock from one lot to another is a line out of one and a line into the other. Needs stock.adjust at the location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdjustStockInput"}}}},"responses":{"201":{"description":"Stock put on hand or corrected by hand, as stock movements.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockAdjustment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/serialized-units":{"get":{"operationId":"listSerializedUnits","summary":"Find serialized units","tags":["Inventory"],"description":"Returns individual units of serial-tracked items, such as hot tubs, oldest first. Search by serial number; filter by item, by location, to units in stock, or to units free to sell (available=true). Each unit says which order or quote has promised it to a customer, if any. listStockMovements with serialized_unit_id gives a unit's history.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only units of this item. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only units in stock at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"in_stock","required":false,"description":"true: only units in stock somewhere. false: only units not in stock (sold, or not yet received)."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"available","required":false,"description":"true: only units free to sell, in stock and promised to no open order or unexpired quote. false: only units in stock that are promised to one."}],"responses":{"200":{"description":"One page of serialized units and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerializedUnitPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createSerializedUnit","summary":"Record a serialized unit","tags":["Inventory"],"description":"Records one unit of a serial-tracked item by its serial number and cost, before it is put into stock with adjustStock (opening stock). Units that arrive on a purchase order are recorded by receiving them; do not create those here. Requires the stock.adjust permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSerializedUnitInput"}}}},"x-required-permission":"stock.adjust","responses":{"201":{"description":"One individual unit of a serial-tracked item, for example one hot tub with its serial number. The unit keeps its identity for life: in stock, sold, installed at a site, serviced under warranty. Its place in stock changes only through stock movements. schema.org: IndividualProduct.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerializedUnit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/serialized-units/{serialized_unit_id}":{"get":{"operationId":"getSerializedUnit","summary":"Get a serialized unit","tags":["Inventory"],"description":"Returns one unit: what it is, its serial number, and where it is in stock if it is.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"serialized_unit_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One individual unit of a serial-tracked item, for example one hot tub with its serial number. The unit keeps its identity for life: in stock, sold, installed at a site, serviced under warranty. Its place in stock changes only through stock movements. schema.org: IndividualProduct.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerializedUnit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateSerializedUnit","summary":"Update a serialized unit","tags":["Inventory"],"description":"Corrects a serial number or notes, or records the options a unit of a model built to order is built with (not while an order has promised it). The cost can be corrected only until the unit first moves into stock; to lower what a unit in stock is worth, use writeDownSerializedUnit. Requires the stock.adjust permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSerializedUnitInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"serialized_unit_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"stock.adjust","responses":{"200":{"description":"One individual unit of a serial-tracked item, for example one hot tub with its serial number. The unit keeps its identity for life: in stock, sold, installed at a site, serviced under warranty. Its place in stock changes only through stock movements. schema.org: IndividualProduct.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerializedUnit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/serialized-units/{serialized_unit_id}/write-down":{"post":{"operationId":"writeDownSerializedUnit","summary":"Write a unit down to what it is worth","tags":["Inventory"],"description":"Lowers the cost of a unit in stock that is worth less than it cost: a display or demo model, a unit with damage. The unit stays where it is; the difference comes off the value of stock and goes to inventory adjustments, as a revaluation in the stock ledger. It then sells, or goes back to the vendor, at its new cost. Only down, never up. Needs stock.adjust where the unit is.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteDownSerializedUnitInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"serialized_unit_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One individual unit of a serial-tracked item, for example one hot tub with its serial number. The unit keeps its identity for life: in stock, sold, installed at a site, serviced under warranty. Its place in stock changes only through stock movements. schema.org: IndividualProduct.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerializedUnit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-lots":{"get":{"operationId":"listStockLots","summary":"See stock by lot","tags":["Inventory"],"description":"Returns the lots of items tracked by lot, the first to expire first, each with how much is on hand at each location. Filter by item, location or lot code (a recall), to lots on hand, or to lots expiring before a date. An item's unknown lot (lot_code null) holds stock whose lot is not known. listStockMovements with stock_lot_id says where a lot came from; listSales with it says who bought it.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only this item's lots. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only lots that have been at this location, with only their stock there. Accepts the record's id or its branch code."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"lot_code","required":false,"description":"Return only lots with this code, of any item."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"expires_before","required":false,"description":"Return only lots that expire before this date. Lots with no expiry date are left out."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"on_hand","required":false,"description":"Send true to leave out lots with nothing on hand (where location_id says, there)."}],"responses":{"200":{"description":"One page of lots and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLotPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-lots/{stock_lot_id}":{"patch":{"operationId":"updateStockLot","summary":"Correct a lot's code or expiry","tags":["Inventory"],"description":"Corrects the lot code or expiry date recorded for a lot, when it was read wrong off the goods. Every movement of the lot stays with it. The unknown lot has neither. Needs stock.adjust. Requires the stock.adjust permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStockLotInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_lot_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"stock.adjust","responses":{"200":{"description":"One lot (batch) of an item tracked by lot: the units a vendor made together and printed one lot code on. Stock of the lot is in stock_lot_level, and which movements took or brought it in stock_movement_lot. schema.org: SomeProducts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLot"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/transfers":{"get":{"operationId":"listStockTransfers","summary":"List stock transfers","tags":["Inventory"],"description":"Returns moves of stock between locations, oldest first. Filter by item or by either location.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only transfers of this item. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only transfers out of or into this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"stock_transfer_batch_id","required":false,"description":"Return only the lines of this transfer of many lines (transferStockBatch)."}],"responses":{"200":{"description":"One page of transfers and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockTransferPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"transferStock","summary":"Move stock to another location","tags":["Inventory"],"description":"Moves stock of one item from one location to another in one step, at exactly the cost it left with. A serialized unit moves on its own, named in serialized_unit_id. For an item tracked by lot, name the lot, or leave it out to move the first to expire; the stock arrives in the lots it left in. To undo a transfer, transfer it back. Needs stock.transfer at the location the stock leaves. An order delivered to the customer's site can be handed over straight from the store that holds the stock (fulfilSalesOrder's location_id), without a transfer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferStockInput"}}}},"responses":{"201":{"description":"Stock of one item moved from one location to another. Rows are only ever added; to undo a transfer, transfer it back. The stock leaves one location and arrives at the other in the same step, at exactly the cost it left with.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockTransfer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts":{"get":{"operationId":"listStockCounts","summary":"List stock counts","tags":["Stock Counts"],"description":"Returns stock counts, oldest first, each with what was counted. Filter by location or status.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only counts at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["open","posted","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only counts in this status."}],"responses":{"200":{"description":"One page of stock counts and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCountPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createStockCount","summary":"Start a stock count","tags":["Stock Counts"],"description":"Starts counting what is on the shelf at one location: all of it, or a few items (a cycle count). Record what is found, now or with recordStockCount, then post it to correct the stock. With covers_units, the count also covers the spas and other serialized units there: scan each one's serial number with recordStockCountUnits, and posting reconciles every unit recorded there.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStockCountInput"}}}},"responses":{"201":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}":{"get":{"operationId":"getStockCount","summary":"Get a stock count","tags":["Stock Counts"],"description":"Returns one count with each item counted, what the system held when it was counted, and the difference posting makes.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}/lines":{"post":{"operationId":"recordStockCount","summary":"Record what was counted","tags":["Stock Counts"],"description":"Records how many of each item were found on the shelf. Counting an item again replaces the figure. What the system held is taken at that moment, so sales made between counting and posting are not undone.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordStockCountInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}/reasons":{"post":{"operationId":"explainStockCount","summary":"Say why items were over or short","tags":["Stock Counts"],"description":"Puts what an open count found over or short down to one of the business's reasons (theft, breakage, ...), item by item, before it is posted. A line with no reason is put down to the business's count reason (count error, by default). Changes nothing counted. Needs stock.adjust at the count's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExplainStockCountInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}/units":{"post":{"operationId":"recordStockCountUnits","summary":"Scan serialized units on a count","tags":["Stock Counts"],"description":"Records the serial numbers of the spas and other serialized units found at the count's location, on a count that covers units. Each is matched to its unit; one the system has never seen is recorded as not known (known false), never created, so check it against the data plate. Scanning a serial again takes afresh where the system has the unit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordStockCountUnitsInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}/units/{stock_count_unit_id}/remove":{"post":{"operationId":"removeStockCountUnit","summary":"Take back a scan","tags":["Stock Counts"],"description":"Takes back a serial number scanned on an open count: scanned in error, or mistyped. It then counts for nothing. Scanning the serial again puts it back.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_unit_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}/post":{"post":{"operationId":"postStockCount","summary":"Post a stock count","tags":["Stock Counts"],"description":"Corrects the stock to what was counted: one movement per item for the difference. Refused when another count has corrected an item since it was counted here; record that item again with recordStockCount first. On a count that covers units, also reconciles each serialized unit (each unit's result says what was done): one found here but recorded elsewhere is moved here by a transfer, one written off earlier comes back into stock, and one recorded here but not scanned is flagged missing, or written off where the business's policy (stock_count_missing_units) says so. A serial no unit has, or a unit sold or sent back, changes nothing and is left on the count to look into. Refused when a unit has moved since it was scanned. Needs stock.adjust at the count's location. Final.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock-counts/{stock_count_id}/cancel":{"post":{"operationId":"cancelStockCount","summary":"Cancel a stock count","tags":["Stock Counts"],"description":"Abandons an open count. Stock is not changed. Needs stock.adjust at the count's location, as posting it does. Final.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_count_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A count of what is actually on the shelf at one location: all of it, or a few items (a cycle count). While open, counted quantities are entered. Posting it corrects the stock to what was counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockCount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/par-levels":{"get":{"operationId":"listParLevels","summary":"List the par levels kept at a location or of an item","tags":["Purchasing"],"description":"Returns the par levels kept: for each item at each location, the par it is brought back up to and the minimum it may fall to first, with what is on hand there now. Filter by location for a store's or a van's par list, or by item for every location's level of it. getReplenishment orders by them; getStockRestock lists a van's restock from them.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only the levels kept at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Only the levels of this item. Accepts the record's id or its SKU."}],"responses":{"200":{"description":"One page of par levels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParLevelPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"setParLevels","summary":"Set how much of items a location keeps: par and minimum","tags":["Purchasing"],"description":"Sets par levels, each an item at a location: the par it is brought back up to, and optionally a minimum (reorder point) it may fall to first; with no minimum it is brought back as soon as it is below par. A whole par list at once: a van's, or the slow movers a store must always have. par_quantity null stops keeping a level. Premises are reordered to par by purchase order (getReplenishment); a van is restocked to par from its home store (getStockRestock). Needs purchase.manage at each location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetParLevelsInput"}}}},"responses":{"200":{"description":"The par levels set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetParLevelsResult"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/restock":{"get":{"operationId":"getStockRestock","summary":"Get what to bring to a van, or any location, to restock it","tags":["Inventory"],"description":"Answers what to load onto a van from its home store: each item it keeps a par level of and holds less of, up to par (below_par, minimum or not: the trip is being made), and each item it keeps no level of that was used on jobs, lost or counted short and not yet replaced (used). Says how many the store has to spare and how many to move. Any location can be restocked from another named in from_location_id. Move it all in one transfer with transferStockBatch.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":true,"description":"The location to restock: a van, usually. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"from_location_id","required":false,"description":"Where the stock comes from. Defaults to the van's home store; required for premises. Accepts the record's id or its branch code."}],"responses":{"200":{"description":"What to bring to a location to restock it, from where.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockRestock"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/transfer-batches/{stock_transfer_batch_id}":{"get":{"operationId":"getStockTransferBatch","summary":"Get a transfer of many lines","tags":["Inventory"],"description":"Returns one transfer of many lines (transferStockBatch): where it went from and to, who moved it, and each line with what it cost. Costs need cost.view.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"stock_transfer_batch_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"Several items moved from one location to another together, as one transfer of many lines: a van restocked from its home store. Its lines are the stock_transfer rows naming it, all moved in the same step. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockTransferBatch"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/transfer-batches":{"post":{"operationId":"transferStockBatch","summary":"Move many items to another location in one transfer","tags":["Inventory"],"description":"Moves several items from one location to another in one step, all or none: a van restocked from its home store (getStockRestock says what), or a store's chemicals sent on from another. Each line moves as transferStock would, at exactly the cost it left with; a serialized unit is a line of its own. listStockTransfers with stock_transfer_batch_id reads the lines again. Needs stock.transfer at the location the stock leaves.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferStockBatchInput"}}}},"responses":{"201":{"description":"Several items moved from one location to another together, as one transfer of many lines: a van restocked from its home store. Its lines are the stock_transfer rows naming it, all moved in the same step. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockTransferBatch"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/stock/aging":{"get":{"operationId":"getStockAging","summary":"Get stock by how long it has been on hand","tags":["Inventory"],"description":"Returns stock on hand per location in the business's age buckets (stock_aging_bucket_days, default 0-90, 91-180, 181-270 and over 270 days), with what it cost and, for units on a floor plan, what is owed and the interest accrued so far. A serialized unit's age counts from when it came into the business, not from a move between locations; quantity stock's from when it arrived at the location. Values need cost.view.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Count only this item. Accepts the record's id or its SKU."}],"responses":{"200":{"description":"Stock on hand by how long it has been on hand, per location, in the business's buckets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockAging"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders":{"get":{"operationId":"listSalesOrders","summary":"Find quotes and orders","tags":["Sales"],"description":"Returns quotes and orders, newest first, with their totals and what is owed or held on deposit. Filter by status, customer, the staff member credited, item, location or order date; search takes an order number or a customer's name. These are booked sales, by the date the order was written; what was handed over or returned, by the day it happened, is in listSales. Orders made at a location are all shown to a key that holds sales.view there; elsewhere a key sees only orders its staff member is credited on, or one customer's (customer_id). One order is always found by its number or id with getSalesOrder.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["quote","open","completed","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only orders in this status."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's orders. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"credited_staff_member_id","required":false,"description":"Return only orders this staff member is credited on (credits): a salesperson's own quotes to follow up."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only orders with a line for this item. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only orders made at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"sales_channel_id","required":false,"description":"Return only orders that came through this channel."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"subscription_id","required":false,"description":"Return only the orders made for this subscription's cycles."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date_from","required":false,"description":"Return only orders dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date_to","required":false,"description":"Return only orders dated on or before this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"opened_from","required":false,"description":"Return only orders the customer agreed to on or after this date: booked sales."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"opened_to","required":false,"description":"Return only orders the customer agreed to on or before this date."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"owes_back","required":false,"description":"true: only orders holding more of the customer's money than they keep (owed_back above 0), as when an order is cancelled or goods come back and the refund has not been given. false: only the others."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_tax_exempt","required":false,"description":"true: only untaxed orders, each naming the certificate it was made on. false: only taxed ones."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"tax_exemption_certificate_id","required":false,"description":"Return only the orders made untaxed on this certificate: what an auditor asks to see."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."}],"responses":{"200":{"description":"One page of sales orders and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrderPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createSalesOrder","summary":"Write up a quote or an order","tags":["Sales"],"description":"Creates a quote, or an open order when the customer has already agreed, with its lines (packages are added with addSalesOrderPackage). Prices default to the items' prices; the database taxes and totals it. A price below the item's, or a discount, needs order.discount at the location, beyond what this key's roles allow (getIdentity, discount_allowances). An order written up open must already meet the business's deposit (min_deposit_percent): to take one, write up a quote and open it with a deposit. Exempting it from tax, taxing it in another area or dating it on another day needs tax.override there. Credit goes to the staff member this key acts for unless credits say otherwise.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSalesOrderInput"}}}},"responses":{"201":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}":{"get":{"operationId":"getSalesOrder","summary":"Get a quote or order whole","tags":["Sales"],"description":"Returns one order with its lines, who is credited, every payment and hand-over, what is owed or held on deposit, and next: what can be done with it now.","parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateSalesOrder","summary":"Update a quote or order","tags":["Sales"],"description":"Changes the fields sent. Where it is delivered, its date and its tax treatment are fixed once goods have been handed over or money taken; changing the site or tax area before then re-taxes the lines. Exempting it from tax, taxing it in another area or dating it on another day needs tax.override at its location; where the business taxes goods where they are picked up (carry_out_elsewhere), an order to be carried out may move to a location's tax area without it when that does not lower its tax, money taken or not, until something is handed over. Who it is for (customer_id) can be named on an order with none at any time, and put right on a sale already under way while nothing is owed on it and it has no delivery site, handed-over serialized unit, work order or subscription; taking such a sale off the customer it names needs order.cancel at its location. Its payments follow the customer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/open":{"post":{"operationId":"openSalesOrder","summary":"Turn a quote into an order","tags":["Sales"],"description":"Records that the customer has agreed to the quote. Goods can then be handed over and payments taken. Send deposit to take the money the customer pays down in the same step. When the business asks for a deposit before an order opens (getPolicy, min_deposit_percent), the quote opens only once that share of its total is paid; a key holding credit.manage at the order's location may open it with less.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenSalesOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/cancel":{"post":{"operationId":"cancelSalesOrder","summary":"Cancel a quote or order","tags":["Sales"],"description":"Records that the sale will not happen. A quote can always be cancelled; an open order needs order.cancel at its location, and only while nothing is with the customer. Money held for the customer is refunded first, except the business's cancellation fee (getPolicy, cancellation_fee_percent, or the items' categories): what is still held when it is cancelled, up to that fee, is kept as fee income (cancellation_fee on the order). Refund everything first to waive it. Final.","parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/lines":{"post":{"operationId":"addSalesOrderLines","summary":"Add lines to a quote or order","tags":["Sales"],"description":"Adds items to a quote or open order. A price below the item's, or a discount, needs order.discount at the order's location, beyond what this key's roles allow (getIdentity, discount_allowances).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSalesOrderLinesInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/lines/{sales_order_line_id}":{"patch":{"operationId":"updateSalesOrderLine","summary":"Change a line on a quote or order","tags":["Sales"],"description":"Changes the fields sent on one line: quantity (0 drops it), price, discount, which serialized unit the customer gets, or the options a model is built with (the unit named must be built so). A package's line takes only its unit and options, and, once part of it is handed over, a call-off of the rest: change the rest with updateSalesOrderPackage. Once some of a line is handed over, only its quantity can change, and only down to quantity_fulfilled: that calls off the rest. A price below the item's, or a discount, needs order.discount, beyond what this key's roles allow (getIdentity, discount_allowances).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesOrderLineInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sales_order_line_id","required":true,"description":"The line, from the order's lines."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/credits":{"post":{"operationId":"setSalesCredits","summary":"Say who gets credit for a sale","tags":["Sales"],"description":"Replaces who is credited with the sale and their shares, which must add up to 1 by the time goods are handed over. Commission is earned on hand-over by whoever is credited then, so once anything is handed over the credit is fixed. Changing the credit the order was written up with needs order.credit at the order's location; giving credit on an order that credits no one, or sending the credit it already has, needs nothing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetSalesCreditsInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/fulfilments":{"post":{"operationId":"fulfilSalesOrder","summary":"Hand goods over to the customer","tags":["Sales"],"description":"Records goods or services handed to the customer: everything still to hand over, or the lines and quantities sent. This is when the sale counts: stock leaves, and revenue, tax, cost and commission are recorded. Money paid earlier stops being a deposit; anything unpaid becomes owed, within the customer's credit limit. A sale with no customer must be paid in full first. The tax can change here, by the business's policy: an order with tax_rate_date hand_over is taxed at today's rates, and one carried out of a location in another tax area is taxed there when the business taxes goods where they are picked up. The order returned carries the new tax_total, total and balance_due to tell the customer. For an item tracked by lot, name the lot handed over, or leave it out to hand over the first lots to expire; the hand-over records which lots left. Handing goods over from a location other than the order's moves them between stores, so it needs stock.transfer at that location: one named in location_id, or the one a serialized unit is at when none is named.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FulfilSalesOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/returns":{"post":{"operationId":"returnSalesOrderGoods","summary":"Take back goods the customer returns","tags":["Sales"],"description":"Records goods coming back from the customer on the order they were sold on: the right call whenever the sale can be found (listSales by item_id, customer_id or date). They return to stock at what they cost, the revenue and tax are taken back, and the line shrinks by what came back. Commission is taken back unless the plan made it final. Money is not given back by this: refund it with refundPayment, as money or to the Store credit method. For an exchange, return the goods and add a new line. Goods with no sale to find go back with returnGoodsWithoutReceipt, if the business takes them. Needs order.return at the location the goods come back to (location_id, or the order's location), wherever they were sold or handed over from. Goods tracked by lot come back into a lot they left from: name it when the line's goods left from more than one. A restocking fee the business keeps (getPolicy) is added to fee_total and comes off the refund; waive_restocking_fee needs order.discount. Past its window (each line's return_window) a return needs order.cancel.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnSalesOrderGoodsInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/payments":{"post":{"operationId":"takePayment","summary":"Take a payment","tags":["Sales"],"description":"Records money received toward an order: a deposit before the goods are handed over, or payment for them after. Omit amount to pay what is left. An order cannot be paid beyond its total, and while a trade-in is agreed, beyond money_due, leaving room for the allowance its unit pays. To spend a customer's store credit (getCustomer, balance.store_credit), pay with the Store credit method (listPaymentMethods, kind store_credit); the order must name that customer, and no more than their balance can be paid with it. To spend a gift card, pay with the Gift card method (kind gift_card), sending gift_card with the card's code (and PIN): at most what it holds.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TakePaymentInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/refunds":{"post":{"operationId":"refundPayment","summary":"Give money back to the customer","tags":["Sales"],"description":"Records money given back on an order, by the method it goes back by. Needs payment.refund where it is paid out (location_id, or the order's location), and at the order's location too for more than the goods returned where it is paid out are owed back, less what was refunded there. A refund gives back only money held for the customer (deposit_held), never leaving them owing: to refund for goods coming back, record their return first. To refund as store credit, use the Store credit method (the business must offer it and the order must name the customer); what was paid in store credit goes back only to store credit. A goodwill credit on goods kept is giveStoreCredit; a payment that never arrived is reversePayment, as is a check handed back uncashed. When the business refunds only to the methods an order was paid with (getPolicy, refund_to_original_method), each method, store credit among them, takes back at most what was paid by it less what went back by it; another method needs ledger.post where the money is paid out. Gift card money goes back onto a card first (Gift card method; gift_card_id or new_gift_card). At most paid_total less trade_in_held: that goes back by giveBackTradeIn.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundPaymentInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments":{"get":{"operationId":"listPayments","summary":"List payments","tags":["Sales"],"description":"Returns payments, refunds and reversals, oldest first. Filter by customer, order, method, location or date: a day's takings at one store is location_id with paid_from and paid_to the same day. Payments taken at a location are all shown to a key that holds sales.view there; elsewhere a key sees only payments on orders its staff member is credited on, every payment on one order (sales_order_id), as getSalesOrder shows them, or one customer's (customer_id).","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only payments on this customer's orders, including any taken before they were named. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only payments on this order. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"payment_method_id","required":false,"description":"Return only payments by this method."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only payments taken at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"paid_from","required":false,"description":"Return only payments dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"paid_to","required":false,"description":"Return only payments dated on or before this date."}],"responses":{"200":{"description":"One page of payments and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{payment_id}/reverse":{"post":{"operationId":"reversePayment","summary":"Record that a payment did not arrive, or hand a check back","tags":["Sales"],"description":"Undoes a payment that turned out not to be money: a check that bounced, a card payment charged back. Handing a customer back their own check before it is banked is this too, not a refund. It goes back by the same method, at the location that took it, and the customer simply owes again, whatever their credit limit. Needs payment.refund at the location that took the payment. Cash taken into a drawer comes back out of that drawer while it is open, and out of the safe it was counted into once it is closed; the safe must still hold it. Returns the reversal; getSalesOrder shows the order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReversePaymentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"payment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"Money a customer paid toward one order, or was refunded from it. Rows are only ever added; a refund is a new row with a negative amount. Money paid before the goods are handed over is a deposit. A payment that never arrived (a bounced check, a card chargeback) is undone by a row that names it in reverses_payment_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales/counter/price":{"post":{"operationId":"priceCounterSale","summary":"Price a counter sale without making it","tags":["Sales"],"description":"Writes nothing. Says what a counter sale of these lines would come to now: each line's price and where it came from (a price list, the item's own price, or a price set by hand), its tax, and the total the payments of recordCounterSale must add up to. Use it to tell a customer the total, or to show it as a ticket is built, before taking any money. It is refused for the same reasons the sale would be: a discount beyond this key's allowance, an item that cannot be sold, a unit that is not available. A line of an item tracked by serial number with no unit named is priced, and says needs_unit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceCounterSaleInput"}}}},"responses":{"200":{"description":"What a counter sale of these lines would come to, priced and taxed as recordCounterSale would price it now. Nothing is written: no order, no number, no stock held.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CounterSalePrice"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales/counter":{"post":{"operationId":"recordCounterSale","summary":"Ring up a sale at the counter","tags":["Sales"],"description":"Records a sale paid for and taken away in one go: opens an order with its lines, takes the payments, and hands everything over at location_id. The payments must add up to the whole total, no more: omit amount on one of them to have it pay what is left. For cash, send what the sale keeps, not what was handed over, and give the rest back as change. A discount needs order.discount at the location, beyond what this key's roles allow. Goods are handed over only from stock at location_id: a unit at another showroom is moved first with transferStock, which needs stock.transfer there. Store credit is a payment by the Store credit method, for a named customer; a gift card is a payment by the Gift card method with gift_card carrying its code.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCounterSaleInput"}}}},"responses":{"201":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/document":{"get":{"operationId":"getQuoteDocument","summary":"Get a quote or order written out for the customer","tags":["Receipts"],"description":"Returns the quote, or the order once opened, as the customer reads it: html, a full page to print or show across the desk, or text. It lists what they are getting (each package with what is in it, units by serial number), delivery or pickup, totals, trade-ins, financing (approved, or applied for and waiting), what the customer paid and what is left for them to pay, and on a quote the date its prices hold through and the deposit the business asks to order (min_deposit_percent). The order's notes are the staff's own and never appear. Built from the order as it is now. To send it to the customer, emailQuote.","parameters":[{"schema":{"default":"html","type":"string","enum":["html","text"]},"in":"query","name":"format","required":false,"description":"html to print, text for a message."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"A quote or order written out for the customer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteDocument"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/emails":{"post":{"operationId":"emailQuote","summary":"Email a quote or order to its customer","tags":["Receipts"],"description":"Writes the quote (or the order, once opened) as getQuoteDocument gives it, as a service email to the order's customer: their primary email address, or the one of theirs named. It goes out only where their consent allows; one held back is message_status suppressed, with the reason. Only to the order's own customer: an order with none is emailed to no one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailQuoteInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"201":{"description":"A quote or order emailed to its customer as a service message: it goes only where their consent allows, and one held back says so (suppressed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEmail"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packages":{"get":{"operationId":"listPackages","summary":"Find packages","tags":["Packages"],"description":"Returns the packages the business sells at one price (a spa with its cover, steps, chemicals and delivery), oldest first, each with its slots, what each slot offers, and what the package saves on its items bought one by one. Search by code or name; item_id finds the packages an item is in.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only packages with this item in a slot, as its own item or a choice. Accepts the record's id or its SKU."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of packages and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackagePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createPackage","summary":"Add a package sold at one price","tags":["Packages"],"description":"Adds a package: the one price it is advertised at and its slots, each with the item it comes with, how many, whether the customer may leave it out (can_drop), and other items they may have instead (choices, each with what it adds to the price). Mark the spa's slot is_main. On an order each slot becomes an ordinary line, and the package price is shared among them by their regular prices, so every item in it needs a price. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePackageInput"}}}},"x-required-permission":"catalog.manage","responses":{"201":{"description":"Goods and services the business advertises and sells together at one price, for example a spa with its cover, cover lifter, steps, starter chemicals and delivery. Each slot is a package_component; on an order each becomes an ordinary line, so every component is stocked, taxed, handed over and returned on its own. schema.org: ProductCollection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Package"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packages/{package_id}":{"get":{"operationId":"getPackage","summary":"Get a package","tags":["Packages"],"description":"Returns one package with its price, its slots in order, the item each comes with, the choices each offers and their price differences, which may be left out, and what it saves on its items bought one by one.","parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"package_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its package code."}],"responses":{"200":{"description":"Goods and services the business advertises and sells together at one price, for example a spa with its cover, cover lifter, steps, starter chemicals and delivery. Each slot is a package_component; on an order each becomes an ordinary line, so every component is stocked, taxed, handed over and returned on its own. schema.org: ProductCollection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Package"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updatePackage","summary":"Update a package","tags":["Packages"],"description":"Changes the fields sent: its code, name, description or price. A new price applies to orders the package is added to from now on; orders that have it keep theirs. Set is_active to false to stop selling it. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePackageInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"package_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its package code."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"Goods and services the business advertises and sells together at one price, for example a spa with its cover, cover lifter, steps, starter chemicals and delivery. Each slot is a package_component; on an order each becomes an ordinary line, so every component is stocked, taxed, handed over and returned on its own. schema.org: ProductCollection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Package"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packages/{package_id}/components":{"post":{"operationId":"setPackageComponent","summary":"Add or change a slot in a package","tags":["Packages"],"description":"Adds a slot to the package, or changes the fields sent on the slot of that name: the item it comes with, how many, whether it may be left out, whether it is the main one, and the other items it offers (choices, added or changed by item). Set is_active to false to take the slot out of the package. Orders that already have the package keep what they have. Returns the package. Requires the catalog.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPackageComponentInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"package_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its package code."}],"x-required-permission":"catalog.manage","responses":{"200":{"description":"Goods and services the business advertises and sells together at one price, for example a spa with its cover, cover lifter, steps, starter chemicals and delivery. Each slot is a package_component; on an order each becomes an ordinary line, so every component is stocked, taxed, handed over and returned on its own. schema.org: ProductCollection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Package"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/packages":{"post":{"operationId":"addSalesOrderPackage","summary":"Sell a package on a quote or order","tags":["Packages"],"description":"Adds a package to a quote or open order at its price now: a line for each slot, with the slot's own item unless components say otherwise (another of its choices, left out, the options the spa is built with, the unit the customer gets). The package price is shared among the lines by their regular prices, so each line shows its price and its share of the saving as its discount; tax, hand-over, returns and commission then work line by line. A discount_amount beyond what this key's roles allow (getIdentity, discount_allowances) needs order.discount at the order's location. Send idempotency_key so that a retry does not add the package twice.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSalesOrderPackageInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/packages/{sales_order_package_id}":{"patch":{"operationId":"updateSalesOrderPackage","summary":"Change a package on a quote or order","tags":["Packages"],"description":"Changes what is in the package's slots (another item the slot offers, a slot left out or kept after all, the options the spa is built with, the unit the customer gets), its discount, or takes the whole package off the order (remove). Every change prices the package again: a choice adds its price difference, and leaving a slot out takes off its share only when the business credits that (getPolicy, package_drop_credit). Once a component is handed over its price is fixed, and later changes are shared by what is still to come; once one comes back, the package is fixed. Options sent for a slot are the whole set the unit is built with, replacing those it had. A discount beyond this key's allowance needs order.discount. What is still to come of a component partly handed over is called off on its line (updateSalesOrderLine, quantity down to quantity_fulfilled).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesOrderPackageInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sales_order_package_id","required":true,"description":"The package on the order, from the order's packages."}],"responses":{"200":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/package-sales":{"get":{"operationId":"getPackageSales","summary":"How many of each package sold, and how often each component was kept","tags":["Packages"],"description":"For packages on orders the customer agreed to in a period (opened_on, by default this month to date): how many sold, how many are fully handed over and how many had goods come back, what they were sold for and what returns gave back, what their goods handed over earned and cost, and for each slot how many kept it (attach_rate) and with which item. A slot brought back whole counts as returned, not kept. Cancelled orders and packages taken off an order do not count. Costs need cost.view. Orders made at a location are counted for a key that holds sales.view there; elsewhere only orders its staff member is credited on.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date_from","required":false,"description":"The first day counted. Defaults to the first of date_to's month."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date_to","required":false,"description":"The last day counted. Defaults to today."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Count only orders made at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"code"},"in":"query","name":"package_id","required":false,"description":"Count only this package. Accepts the record's id or its package code."}],"responses":{"200":{"description":"How many of each package sold in a period, what they were sold for and earned, and how often customers kept each component.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackageSales"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/trade-ins":{"post":{"operationId":"addTradeIn","summary":"Take a customer's old unit in toward a quote or order","tags":["Trade-Ins"],"description":"Records an old unit (a hot tub, a swim spa) the customer trades in toward this quote or order, and what they are allowed for it. The order must name the customer. item_id is what the unit comes into stock as: a good tracked by serial number, for example a used spa item. The allowance is paid toward the order when the unit is received (receiveTradeIn), and the unit comes into stock at it; the sale keeps its full price. Where a tax jurisdiction gives a trade-in credit (its trade_in_credit), the allowance comes off the taxed price at once, and a line taxed less is handed over only once the unit is received. Needs trade_in.appraise at the order's location. Returns the trade.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTradeInInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"The quote or order, or its number. Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"201":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trade-ins":{"get":{"operationId":"listTradeIns","summary":"Find trade-ins, and the traded units out back","tags":["Trade-Ins"],"description":"Returns trade-ins, newest written up first, in every filter (by id, so no two tie and pages never overlap): what was agreed, what came in and where each unit is now. in_stock=true lists the traded units still in stock (to sell, write down or haul away); received_on says how long each has been there. Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on. A trade is at its order's location. One customer's or one order's are always shown.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["agreed","received","given_back","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only trades in this status."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only the trades toward this order. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's trades. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only trades whose unit is in stock at this location now. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"in_stock","required":false,"description":"true: only units still in stock. false: only trades whose unit is not in stock."}],"responses":{"200":{"description":"One page of trade-ins.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeInPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trade-ins/{trade_in_id}":{"get":{"operationId":"getTradeIn","summary":"Get a trade-in","tags":["Trade-Ins"],"description":"Returns one trade-in: what was agreed, where the unit is now and what it is in stock at (with cost.view), and next: what can be done with it now.","parameters":[{"schema":{"type":"string","x-natural-key":"trade_in_number"},"in":"path","name":"trade_in_id","required":true,"description":"The trade-in, or its number. Accepts the record's id or its trade-in number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateTradeIn","summary":"Appraise a trade-in again, or correct its details","tags":["Trade-Ins"],"description":"Changes the fields sent. The allowance, item and equipment change only while the trade is agreed and its order is a quote or open; a new allowance moves the order's tax where a jurisdiction gives a trade-in credit. Needs trade_in.appraise at the order's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTradeInInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"trade_in_number"},"in":"path","name":"trade_in_id","required":true,"description":"The trade-in, or its number. Accepts the record's id or its trade-in number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trade-ins/{trade_in_id}/receive":{"post":{"operationId":"receiveTradeIn","summary":"Receive a traded-in unit, paying its allowance toward the order","tags":["Trade-Ins"],"description":"The unit is taken (picked up by the delivery crew, or dropped off): it comes into stock at location_id (the order's location by default) at its allowance, and the allowance is paid toward the order by the Trade-in payment method, held as a deposit until the goods are handed over. The order must be open, and the allowance no more than is left to pay on it. Sent again for a trade already received, it changes nothing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveTradeInInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"trade_in_number"},"in":"path","name":"trade_in_id","required":true,"description":"The trade-in, or its number. Accepts the record's id or its trade-in number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trade-ins/{trade_in_id}/cancel":{"post":{"operationId":"cancelTradeIn","summary":"Call off a trade-in: the customer keeps the old unit","tags":["Trade-Ins"],"description":"Cancels an agreed trade: nothing is paid by it, and where a tax jurisdiction gave a trade-in credit the order is taxed in full again. Cancelling an order cancels its agreed trades with it. A unit already received is given back instead (giveBackTradeIn). Needs trade_in.appraise at the order's location.","parameters":[{"schema":{"type":"string","x-natural-key":"trade_in_number"},"in":"path","name":"trade_in_id","required":true,"description":"The trade-in, or its number. Accepts the record's id or its trade-in number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trade-ins/{trade_in_id}/give-back":{"post":{"operationId":"giveBackTradeIn","summary":"Give a received unit back to the customer, refunding its allowance","tags":["Trade-Ins"],"description":"The sale was called off or the goods returned, and the customer takes their old unit home: it leaves stock at what it came in at and its allowance is refunded by the Trade-in method. The order must hold at least the allowance (return the goods first), and the unit must still be in stock at its allowance; one written down, sold or disposed of is not given back: refund the allowance as money instead (refundPayment). Needs payment.refund where the unit is.","parameters":[{"schema":{"type":"string","x-natural-key":"trade_in_number"},"in":"path","name":"trade_in_id","required":true,"description":"The trade-in, or its number. Accepts the record's id or its trade-in number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trade-ins/{trade_in_id}/dispose":{"post":{"operationId":"disposeTradeIn","summary":"Haul a traded-in unit away or scrap it","tags":["Trade-Ins"],"description":"Takes a received unit out of stock without a sale (hauled to the dump, scrapped for parts), with the reason: what it is in stock at goes to Inventory Adjustments. A unit worth something less than its allowance is written down instead (writeDownSerializedUnit), and one sold goes on an order line. Sent again with the same reason, it changes nothing. Needs stock.adjust where the unit is.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisposeTradeInInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"trade_in_number"},"in":"path","name":"trade_in_id","required":true,"description":"The trade-in, or its number. Accepts the record's id or its trade-in number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's old unit (their old hot tub) taken in toward a sale, and what they are allowed for it. The allowance pays toward the order by the Trade-in payment method when the unit is received, and the unit comes into stock at it, so the sale itself keeps its full price. Whether it lowers the taxed price is each tax jurisdiction's setting (trade_in_credit). schema.org has no type for it; item_condition follows schema.org itemCondition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeIn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales":{"get":{"operationId":"listSales","summary":"Find what was sold and returned, with totals","tags":["Sales"],"description":"Returns every hand-over and return, newest first, one row per item per occasion: the facts a sale counts on, goods taken back without a receipt included (kind receiptless_return, with no order), dated when the goods changed hands, not when the order was written. Each names the order, the customer, the item and its price. Totals cover every match, so one call answers what sold at a location today, what a customer bought last time or spent this year net of returns, or who bought an item. Filtered by lot, it answers who bought a lot, net of returns, for a recall. Costs and margins are shown where the key holds cost.view at the store that made the sale. Every sale a store made, wherever the goods left from, is shown to a key that holds sales.view there, as getSalesReport counts them; elsewhere a key sees only sales on orders its staff member is credited on, or one customer's (customer_id). Orders written but not yet handed over (booked sales) are in listSalesOrders.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's purchases and returns. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only this item: who bought it, when, and at what price. Accepts the record's id or its SKU."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"category_id","required":false,"description":"Return only items filed under this category or any category below it."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only what left from or came back to this location's stock, whichever store sold it. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"sold_at_location_id","required":false,"description":"Return only sales this store made: its own orders, wherever the goods left from, and goods it took back without a receipt. Its income is in the store's books and its reports. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only what this staff member handed over or took back."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"credited_staff_member_id","required":false,"description":"Return only sales on orders this staff member has credit for. Amounts are the whole sale, not their share."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only what was handed over or returned on this order. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"stock_lot_id","required":false,"description":"Return only hand-overs and returns of this lot: who bought it, for a recall."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"lot_code","required":false,"description":"Return only hand-overs and returns of a lot with this code, of any item; add item_id for one item's."},{"schema":{"type":"string","enum":["hand_over","return","receiptless_return"]},"in":"query","name":"kind","required":false,"description":"Return only hand-overs, only returns on orders, or only goods taken back without a receipt."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"fulfilled_from","required":false,"description":"Return only what happened on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"fulfilled_to","required":false,"description":"Return only what happened on or before this date."}],"responses":{"200":{"description":"One page of hand-overs and returns, the cursor for the next, and totals over them all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/returns/{receiptless_return_id}":{"get":{"operationId":"getReceiptlessReturn","summary":"Get a return made without a receipt","tags":["Sales"],"description":"Returns one return made with returnGoodsWithoutReceipt: what came back, what it was valued at, and how much was refunded by which method. listSales with kind receiptless_return finds them.","parameters":[{"schema":{"type":"string","x-natural-key":"return_number"},"in":"path","name":"receiptless_return_id","required":true,"description":"The return, by id or by its return number. Accepts the record's id or its return number (as a number or a string of digits)."}],"responses":{"200":{"description":"Goods a customer brought back that were not sold on an order the system can find, and what they were given for them. Revenue, tax and cost are taken back as for a return on an order, and the refund goes out in the same step. Rows are only ever added. Allowed only as far as the business's policy says (tenant_policy.receiptless_returns).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptlessReturn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/returns":{"post":{"operationId":"returnGoodsWithoutReceipt","summary":"Take goods back with no sale to return them on, and refund them","tags":["Sales"],"description":"For goods the customer brings back when no order for them can be found: a bottle of shock bought for cash, an item from before this system. First look for the sale with listSales (item_id, customer_id, dates); if it is found, use returnSalesOrderGoods on its order and then refundPayment, which gives back what was actually paid. This operation takes the goods back and refunds in one step, as the business's policy allows (getPolicy, receiptless_*): it may be refused outright, limited to store credit for a named customer, capped per return, and needs the permission the policy names at location_id. The goods are valued at the item's price now, or the lowest it recently sold for; tax is given back only if the policy says so. restock false for goods that cannot be sold again. Goods tracked by lot name the lot printed on them (lot_code). Returns the return with what was refunded.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnGoodsWithoutReceiptInput"}}}},"responses":{"201":{"description":"Goods a customer brought back that were not sold on an order the system can find, and what they were given for them. Revenue, tax and cost are taken back as for a return on an order, and the refund goes out in the same step. Rows are only ever added. Allowed only as far as the business's policy says (tenant_policy.receiptless_returns).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptlessReturn"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/registers":{"get":{"operationId":"listRegisters","summary":"List registers","tags":["Counter"],"description":"Returns the registers (tills) at the business's counters, oldest first, each with the drawer open on it now, if any. Filter by location_id to find where to open a drawer or take cash.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only registers at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of registers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createRegister","summary":"Add a register","tags":["Counter"],"description":"Adds a register (a till with a cash drawer) at a location. Needs cash.manage at the location. A drawer is then opened on it each day with openCashDrawer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRegisterInput"}}}},"responses":{"201":{"description":"One till at a counter: a cash drawer that one person at a time opens, takes cash into, and closes with a count. Each drawer from opening to its count is a cash_drawer_session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Register"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/registers/{register_id}":{"patch":{"operationId":"updateRegister","summary":"Rename or retire a register","tags":["Counter"],"description":"Changes a register's name or code, or retires it (is_active false), which needs its drawer closed first. Needs cash.manage at the register's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRegisterInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"register_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One till at a counter: a cash drawer that one person at a time opens, takes cash into, and closes with a count. Each drawer from opening to its count is a cash_drawer_session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Register"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-paid-reasons":{"get":{"operationId":"listCashPaidReasons","summary":"List reasons cash is paid in or out","tags":["Counter"],"description":"Returns the reasons cash may go into or out of a drawer other than by a sale, oldest first: each with its direction and the account it posts to. Choose one for payOutCash or payInCash.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["in","out"]},"in":"query","name":"direction","required":false,"description":"Return only reasons cash is paid in, or out."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of reasons cash is paid in or out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashPaidReasonPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createCashPaidReason","summary":"Add a reason cash is paid in or out","tags":["Counter"],"description":"Adds a reason cash may be paid out of a drawer (direction out: fuel, postage, a small repair) or into one (direction in: petty cash returned), and the account the other side posts to, usually an expense. Not a control account. For the whole business. Requires the cash.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCashPaidReasonInput"}}}},"x-required-permission":"cash.manage","responses":{"201":{"description":"Why cash goes into or out of a drawer other than by a sale, for example \"Fuel for delivery truck\" (out, to a vehicle expense account) or \"Petty cash returned\" (in). Names the account the other side posts to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashPaidReason"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-paid-reasons/{cash_paid_reason_id}":{"patch":{"operationId":"updateCashPaidReason","summary":"Change or retire a reason cash is paid in or out","tags":["Counter"],"description":"Renames a reason, points it at another account, says whether a note is required, or retires it (is_active false). Its direction is fixed. Cash already paid for it stays posted where it was. Requires the cash.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCashPaidReasonInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"cash_paid_reason_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cash.manage","responses":{"200":{"description":"Why cash goes into or out of a drawer other than by a sale, for example \"Fuel for delivery truck\" (out, to a vehicle expense account) or \"Petty cash returned\" (in). Names the account the other side posts to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashPaidReason"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/registers/{register_id}/cash-drawers":{"post":{"operationId":"openCashDrawer","summary":"Open a cash drawer","tags":["Counter"],"description":"Opens a drawer on a register with its float, the cash put in to make change, which comes out of the location's safe (fundCashSafe puts cash in it). Needs a role at the register's location or everywhere; a float beyond the business's limit (getPolicy, cash_float_limit) needs cash.manage there. One drawer is open on a register at a time: if one is, this is refused and names it. Cash taken at the location then names the drawer (cash_drawer_session_id) on takePayment, recordCounterSale, refundPayment and returnGoodsWithoutReceipt.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenCashDrawerInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"register_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers":{"get":{"operationId":"listCashDrawers","summary":"List cash drawers","tags":["Counter"],"description":"Returns drawers, newest first: status open for those taking cash now (to name on a cash payment), closed for those waiting for a manager to reconcile them. What each should hold is hidden from a key without cash.manage at its location when the business counts blind. With cash.manage, totals say how far over or short all the drawers that match were, per person who opened them: opened_from and opened_to for a month.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only drawers at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"register_id","required":false,"description":"Return only drawers on this register."},{"schema":{"type":"string","enum":["open","closed","reconciled"]},"in":"query","name":"status","required":false,"description":"Return only drawers in this status."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"opened_from","required":false,"description":"Return only drawers opened on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"opened_to","required":false,"description":"Return only drawers opened on or before this date."}],"responses":{"200":{"description":"One page of drawers, and how far over or short they all were.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawerPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers/{cash_drawer_session_id}":{"get":{"operationId":"getCashDrawer","summary":"Get a cash drawer","tags":["Counter"],"description":"Returns a drawer: who opened it and with what float, its count once closed, and with cash.manage at its location (or when the business does not count blind) what it should hold, what moved through it by kind, its entries and how far over or short it was. next says what the drawer allows now.","parameters":[{"schema":{"type":"string","x-natural-key":"session_number"},"in":"path","name":"cash_drawer_session_id","required":true,"description":"The drawer, by its id or its number. Accepts the record's id or its drawer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers/{cash_drawer_session_id}/paid-outs":{"post":{"operationId":"payOutCash","summary":"Pay cash out of a drawer for a reason","tags":["Counter"],"description":"Takes cash out of an open drawer for something other than a sale (fuel, postage, a small repair), posting it to the reason's account (listCashPaidReasons, direction out). A note is needed when the reason says so. Needs a role at the drawer's location or everywhere; more than the business's limit at once (getPolicy, cash_paid_out_limit) needs cash.manage there. Not for refunds: those are refundPayment with the drawer named. Returns the drawer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayCashInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"session_number"},"in":"path","name":"cash_drawer_session_id","required":true,"description":"The drawer, by its id or its number. Accepts the record's id or its drawer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers/{cash_drawer_session_id}/paid-ins":{"post":{"operationId":"payInCash","summary":"Take cash into a drawer for a reason","tags":["Counter"],"description":"Puts cash into an open drawer for something other than a sale (petty cash returned, change bought from next door), posting it to the reason's account (listCashPaidReasons, direction in). Not for a customer paying: that is takePayment with the drawer named. Needs a role at the drawer's location or everywhere; beyond the business's limit (getPolicy, cash_paid_out_limit) needs cash.manage there. Returns the drawer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayCashInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"session_number"},"in":"path","name":"cash_drawer_session_id","required":true,"description":"The drawer, by its id or its number. Accepts the record's id or its drawer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers/{cash_drawer_session_id}/drops":{"post":{"operationId":"dropCash","summary":"Drop cash from a drawer into the safe","tags":["Counter"],"description":"Takes cash out of an open drawer into the location's safe, so the drawer does not hold too much during the day. The drawer stays open. Needs a role at the drawer's location or everywhere. Refused for more than the drawer holds by its records. Returns the drawer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropCashInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"session_number"},"in":"path","name":"cash_drawer_session_id","required":true,"description":"The drawer, by its id or its number. Accepts the record's id or its drawer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers/{cash_drawer_session_id}/close":{"post":{"operationId":"closeCashDrawer","summary":"Count and close a cash drawer","tags":["Counter"],"description":"Closes an open drawer with the cash counted in it (counted_cash, or the count by denomination, which the business may require: getPolicy, cash_count_by_denomination). All of it goes into the safe. Within the business's tolerance (cash_over_short_tolerance) the drawer is reconciled at once; otherwise it waits, closed, for a manager (reconcileCashDrawer). When the business counts blind, how far over or short it was is shown only to a key holding cash.manage at the location. Needs a role at the drawer's location or everywhere. Safe to retry: sent again after it closed with the same count, it answers with the drawer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseCashDrawerInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"session_number"},"in":"path","name":"cash_drawer_session_id","required":true,"description":"The drawer, by its id or its number. Accepts the record's id or its drawer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/cash-drawers/{cash_drawer_session_id}/reconcile":{"post":{"operationId":"reconcileCashDrawer","summary":"Reconcile a drawer closed over or short","tags":["Counter"],"description":"Settles a closed drawer that was counted over or short by more than the business allows, with a note of what was found. Needs cash.manage at the drawer's location. When the cash is counted again (recounted_cash, or the count by denomination) and comes to another figure, the difference goes into or out of the safe; the count it was closed with stands as it was. Safe to retry: sent again with the same note and recount, it answers with the drawer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReconcileCashDrawerInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"session_number"},"in":"path","name":"cash_drawer_session_id","required":true,"description":"The drawer, by its id or its number. Accepts the record's id or its drawer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One drawer in a register from the moment it is opened with its float to the moment its cash is counted out into the safe. Cash taken, given back, paid in or out and dropped is its ledger (cash_drawer_entry). Opened by adding a row; closed and reconciled by changing its status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDrawer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/locations/{location_id}/cash-safe":{"get":{"operationId":"getCashSafe","summary":"See what a location's safe holds","tags":["Counter"],"description":"Returns the cash in a location's safe by its records, the drawers open there and what each should hold, and the safe's latest 50 entries in the order they were applied. Needs cash.manage at the location.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"200":{"description":"The safe at one location and the cash it holds. Derived: kept in step with cash_safe_entry by the database and never written directly. A location with no row has never had cash in its safe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashSafe"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/locations/{location_id}/cash-safe/funds":{"post":{"operationId":"fundCashSafe","summary":"Put cash in a location's safe","tags":["Counter"],"description":"Records cash put into the safe from outside the drawers: a change order, cash drawn from the bank for change, or the cash already there when the business starts keeping its safe here (from Opening Balance Equity, 3900). Posts it against the account named (ledger_account_id, not a control account). Needs cash.manage at the location; cash from an account not marked as a bank account (is_bank_account) is a manual entry and needs ledger.post there too. Returns the safe.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FundCashSafeInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"200":{"description":"The safe at one location and the cash it holds. Derived: kept in step with cash_safe_entry by the database and never written directly. A location with no row has never had cash in its safe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashSafe"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/locations/{location_id}/bank-deposits":{"post":{"operationId":"recordBankDeposit","summary":"Take cash and checks to the bank","tags":["Counter"],"description":"Records one deposit from a location to the bank: the cash taken out of its safe, the checks it carries (from getCashSafe's checks_to_bank) and the bag or slip number. A check is banked once; a check taken elsewhere, reversed, voided or banked already is refused. Into a bank account (is_bank_account) the checks move there with the cash; into Undeposited Funds (1050) only the cash moves, and the accounting system records the bank deposit. Refused for more cash than the safe holds. Needs cash.manage at the location; an account that is neither needs ledger.post. Returns the deposit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordBankDepositInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"201":{"description":"One deposit taken from a location to the bank: the cash out of its safe and the checks taken there, under one bag or slip number. Rows are only ever added; the checks it carries are named when it is recorded (bank_deposit_check).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankDeposit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listBankDeposits","summary":"List a location's bank deposits","tags":["Counter"],"description":"Returns the deposits taken from a location to the bank, newest first, each with the checks it carried. Needs cash.manage at the location.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"deposited_from","required":false,"description":"Return only deposits on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"deposited_to","required":false,"description":"Return only deposits on or before this date."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"200":{"description":"One page of a location's bank deposits, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankDepositPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/bank-deposits/{bank_deposit_id}":{"get":{"operationId":"getBankDeposit","summary":"See a bank deposit","tags":["Counter"],"description":"Returns one deposit taken to the bank: the cash, the checks it carried and the bag number, for the slip. Needs cash.manage at its location.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"bank_deposit_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One deposit taken from a location to the bank: the cash out of its safe and the checks taken there, under one bag or slip number. Rows are only ever added; the checks it carries are named when it is recorded (bank_deposit_check).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankDeposit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/locations/{location_id}/cash-safe/counts":{"post":{"operationId":"countCashSafe","summary":"Count a location's safe","tags":["Counter"],"description":"Records a count of the cash in the safe. A figure other than what the safe holds by its records moves the safe to the count, the difference posted to Cash Over and Short; a count that agrees is kept as a record that it was checked. Needs cash.manage at the location. Returns the safe.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CountCashSafeInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"path","name":"location_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its branch code."}],"responses":{"200":{"description":"The safe at one location and the cash it holds. Derived: kept in step with cash_safe_entry by the database and never written directly. A location with no row has never had cash in its safe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashSafe"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/commission-plans":{"get":{"operationId":"listCommissionPlans","summary":"List commission plans","tags":["Commission"],"description":"Returns how salespeople are paid, oldest first: each plan's basis, rate, category rates and how many are on it.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of commission plans.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionPlanPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createCommissionPlan","summary":"Create a commission plan","tags":["Commission"],"description":"Defines how salespeople are paid: a rate on revenue or gross profit, with other rates for some categories, and the shop's rules for it: whether commission waits for the order to be paid in full, how long a return can take it back, and whether it can go below zero. Left out, each rule pays as on hand-over, always taken back, negative allowed. Put staff on it with updateStaffMember (commission_plan_id). Requires the staff.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommissionPlanInput"}}}},"x-required-permission":"staff.manage","responses":{"201":{"description":"How a salesperson is paid on what they sell, for example \"Showroom sales: 20% of gross profit\". Staff are put on a plan; the plan in force when goods are handed over is the one that pays.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionPlan"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/commission-plans/{commission_plan_id}":{"patch":{"operationId":"updateCommissionPlan","summary":"Change a commission plan","tags":["Commission"],"description":"Changes the fields sent. A new rate or rule pays on goods handed over from now on; commission already worked out keeps the rate and rules it was worked out under. Retire a plan with is_active false. Requires the staff.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommissionPlanInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"commission_plan_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"staff.manage","responses":{"200":{"description":"How a salesperson is paid on what they sell, for example \"Showroom sales: 20% of gross profit\". Staff are put on a plan; the plan in force when goods are handed over is the one that pays.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionPlan"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/commission-entries":{"get":{"operationId":"listCommissionEntries","summary":"List commission earned","tags":["Commission"],"description":"Returns commission entries, oldest first: what was earned on each hand-over, taken back on each return, held until an order is paid in full and earned when it is. Pay counts the entries that are not held. Without commission.view a key sees only the commission of the staff member it acts for.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only this person's commission."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only commission on this order. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"earned_from","required":false,"description":"Return only entries earned on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"earned_to","required":false,"description":"Return only entries earned on or before this date."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_held","required":false,"description":"true for only held entries (not yet earned), false for only those pay counts."}],"responses":{"200":{"description":"One page of commission entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionEntryPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/commission-totals":{"get":{"operationId":"getCommissionTotals","summary":"Add up commission for a period","tags":["Commission"],"description":"Returns each person's commission for a period, net of returns: what payroll pays. Commission held until an order is paid in full counts on the day it is, and is shown apart as held. Without commission.view a key sees only its own staff member's total.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"earned_from","required":true,"description":"First day to count."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"earned_to","required":true,"description":"Last day to count, inclusive."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Count only this person."}],"responses":{"200":{"description":"Commission earned in a period, per person: what payroll pays, and what is held.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommissionTotals"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-quotas":{"get":{"operationId":"listSalesQuotas","summary":"See sales targets and how far along they are","tags":["Commission"],"description":"Returns sales targets with what has been reached so far, added up from goods handed over in each period. Filter by person, location, or a date the period covers.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only this person's targets."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only this location's targets. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"covering","required":false,"description":"Return only targets whose period includes this date."},{"schema":{"type":"string","enum":["revenue","gross_profit","units"]},"in":"query","name":"measure","required":false,"description":"Return only targets on this measure."}],"responses":{"200":{"description":"One page of sales targets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesQuotaPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createSalesQuota","summary":"Set a sales target","tags":["Commission"],"description":"Sets a target for a person, a location, or a person at a location, over a period: revenue, gross profit or units, optionally for one category. Requires the staff.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSalesQuotaInput"}}}},"x-required-permission":"staff.manage","responses":{"201":{"description":"A sales target for one person, one location, or one person at one location, over a period: \"Pat: 60,000 of revenue in October\", \"Boise: 12 hot tubs this quarter\". Attainment is not stored; it is added up from sales_fulfilment for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesQuota"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-quotas/{sales_quota_id}":{"patch":{"operationId":"updateSalesQuota","summary":"Change a sales target","tags":["Commission"],"description":"Changes the fields sent on a target. Requires the staff.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesQuotaInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sales_quota_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"staff.manage","responses":{"200":{"description":"A sales target for one person, one location, or one person at one location, over a period: \"Pat: 60,000 of revenue in October\", \"Boise: 12 hot tubs this quarter\". Attainment is not stored; it is added up from sales_fulfilment for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesQuota"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/receivables":{"get":{"operationId":"listReceivables","summary":"List what customers owe, order by order and on account","tags":["Receivables"],"description":"Returns every order with money owed for goods already handed over (kind order), every part of what a customer owes on account with no order behind it (kind on_account: a payment on account that did not arrive after its money had gone back to them), and every finance charge still owed (kind finance_charge), each with when it fell due and how many days late it is. They add up to Accounts Receivable. Filter to one customer, one location, or what is overdue; take payment on an order with takePayment, and toward what is owed on account with takeAccountPayment. Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on. Filtered to one customer, everything they owe is shown.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only what this customer owes. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only orders made at this location, and debts on account recorded there. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"overdue","required":false,"description":"Send true for only what is past due."}],"responses":{"200":{"description":"One page of what customers owe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceivablePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customer-balances":{"get":{"operationId":"listCustomerBalances","summary":"List who owes money, or holds a deposit","tags":["Receivables"],"description":"Returns each customer who owes money or has paid ahead (a deposit, or money on account not yet applied), with how much is late and their credit terms: the aged list a statement run or a collections call starts from. Filter to customers with something overdue. Requires the receivables.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"overdue","required":false,"description":"Send true for only customers with something past due."}],"x-required-permission":"receivables.view","responses":{"200":{"description":"One page of customer balances.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerBalancePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/layaways":{"get":{"operationId":"listLayaways","summary":"List orders on layaway, or those due or late","tags":["Layaways"],"description":"Returns open orders on layaway, the next payment due soonest first, each with its schedule and where it stands. due=late returns only those with a payment past its day; due=soon those late or with a payment due within within_days (7 by default). Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on. Payments are taken with takePayment.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only layaways sold at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's layaways. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["late","soon"]},"in":"query","name":"due","required":false,"description":"late: only layaways with a payment past its day. soon: those, and those with a payment due within within_days."},{"schema":{"default":7,"type":"integer","minimum":0,"maximum":90},"in":"query","name":"within_days","required":false,"description":"For due=soon: how many days ahead counts as soon."}],"responses":{"200":{"description":"One page of layaways.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LayawayPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/layaway":{"get":{"operationId":"getLayaway","summary":"Get an order's layaway","tags":["Layaways"],"description":"Returns the order's layaway: its schedule, what of each installment is paid, what is late, what is left before the goods go home, and what cancelling it would keep. 404 when the order is not on layaway.","parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"An order on layaway: its schedule of payments and where it stands.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Layaway"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"setLayawaySchedule","summary":"Put an order on layaway, or change its payment schedule","tags":["Layaways"],"description":"Puts an open order for a named customer, with nothing handed over yet, on layaway with the payments agreed, or replaces its schedule (new dates after a missed payment). The installments add up to the order's total. From then its goods go home only once it is paid in full; each payment is taken with takePayment and held as a deposit. Cancelling it (cancelSalesOrder, after refunding the rest) keeps the business's layaway fee, none by default.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetLayawayScheduleInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"An order on layaway: its schedule of payments and where it stands.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Layaway"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/store-credit":{"get":{"operationId":"listStoreCredit","summary":"See store credit given and spent","tags":["Receivables"],"description":"Returns store credit entries, newest first: credit given (a refund to store credit, a return without a receipt, a goodwill credit) and credit spent paying for orders, with customer_id for one customer's history. What a customer has to spend now is getCustomer's balance.store_credit.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's store credit. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["goodwill","refund","redemption","receiptless_return"]},"in":"query","name":"kind","required":false,"description":"Return only entries of this kind."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only credit given or spent at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"entered_from","required":false,"description":"Return only entries dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"entered_to","required":false,"description":"Return only entries dated on or before this date."}],"responses":{"200":{"description":"One page of store credit given and spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreCreditEntryPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/store-credit":{"post":{"operationId":"giveStoreCredit","summary":"Give a customer store credit as goodwill or a price adjustment","tags":["Receivables"],"description":"Adds store credit to a customer's balance with nothing coming back: goodwill, or a price adjustment on goods they keep. It reduces sales income at location_id through Sales Allowances. Needs payment.refund at location_id, and a business that gives store credit (getPolicy, store_credit_offered). Not for goods coming back: return those on their order (returnSalesOrderGoods) and refund to the Store credit payment method, or use returnGoodsWithoutReceipt. The customer spends it with takePayment or recordCounterSale, paying with the Store credit method. Returns the entry, with the balance after it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiveStoreCreditInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"The customer the credit is for. Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"201":{"description":"One change in a customer's store credit: credit given (a refund to store credit, a return without a receipt, a goodwill credit) or spent (a payment with store credit). This is the store credit ledger: rows are only ever added. The application writes only goodwill credits; the others are written by the payment or return that caused them.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreCreditEntry"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/account-payments":{"get":{"operationId":"listAccountPayments","summary":"List payments on account, refunds of account credit and reversals","tags":["Receivables"],"description":"Returns money customers paid on account, account credit given back, and payments on account reversed, oldest first, each with what it applied to which orders and the customer's account credit now. Filter by customer, location, method or date: a day's checks taken on account at one store is location_id with paid_from and paid_to the same day. Those taken at a location are all shown to a key that holds sales.view there; elsewhere a key sees one customer's (customer_id) and nothing else.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["received","refund","reversal"]},"in":"query","name":"kind","required":false,"description":"Return only this kind."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only those taken or given back at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"payment_method_id","required":false,"description":"Return only those by this method."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"paid_from","required":false,"description":"Return only those dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"paid_to","required":false,"description":"Return only those dated on or before this date."}],"responses":{"200":{"description":"One page of payments on account, refunds of account credit and reversals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountPaymentPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/account-credit":{"get":{"operationId":"listAccountCredit","summary":"See how customers' account credit came and went","tags":["Receivables"],"description":"Returns account credit entries, newest first: money paid on account, applied to an order, moved back off one, refunded and reversed, each with the balance after it and where it came from (the payment on account and its check number, or the order). With customer_id it is that customer's credit ledger, in the order the entries were applied: the newest balance_after is their credit now. Entries made at a location are all shown to a key that holds sales.view there; elsewhere a key sees one customer's (customer_id) and nothing else.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's entries. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["received","refunded","reversed","applied","unapplied","gift_card_reversed"]},"in":"query","name":"kind","required":false,"description":"Return only entries of this kind."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only entries made at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"entered_from","required":false,"description":"Return only entries dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"entered_to","required":false,"description":"Return only entries dated on or before this date."}],"responses":{"200":{"description":"One page of account credit entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCreditEntryPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/account-payments":{"post":{"operationId":"takeAccountPayment","summary":"Take a payment toward what a customer owes, without naming one order","tags":["Receivables"],"description":"Records money a customer paid on account: a builder paying last month's statement, a check that covers several invoices. By default (getPolicy, account_payment_application) it pays the orders they owe on, the one due first first; send applications to choose the orders (a deposit on an open order among them), or apply none to keep it. Whatever is not applied stays as account credit, which is the customer's money: applyAccountCredit spends it later, refundAccountCredit gives it back. Cash goes into a drawer (cash_drawer_session_id) like any cash. For a payment toward one order, takePayment is simpler. Returns the payment, what it applied, and the customer's credit and what they owe after.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TakeAccountPaymentInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"The customer who paid. Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"201":{"description":"Money a customer paid toward what they owe without naming one order, or given back from their account credit. Positive when received; negative for a refund of account credit, or for a reversal of a payment that never arrived (a bounced check). What is received is applied to their orders by payments of the On account method, and the rest is their account credit. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountPayment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/account-credit/applications":{"post":{"operationId":"applyAccountCredit","summary":"Apply a customer's account credit to what they owe","tags":["Receivables"],"description":"Spends money the customer paid on account (getCustomer, balance.account_credit) on their orders: with no applications, on what they owe for goods handed over, the order due first first, up to amount or all of it; with applications, on the orders chosen, each up to what it has left to pay (a deposit on an open order among them). Each is a payment by the On account method on the order. Returns what was applied, the credit left and what is still owed; refused when nothing can be applied (no credit, or nothing owed and no order named). To put money held on an order back into account credit, refundPayment it to the On account method.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyAccountCreditInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"The customer whose credit is applied. Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"Account credit applied to a customer's orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCreditApplied"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/account-credit/refunds":{"post":{"operationId":"refundAccountCredit","summary":"Give a customer back money they paid on account","tags":["Receivables"],"description":"Gives back account credit as money, by a cash, check, card or other method: at most the customer's account credit (getCustomer, balance.account_credit). Needs payment.refund at location_id. Cash comes out of a drawer (cash_drawer_session_id) like any cash. When the business refunds only by the methods money came in by (getPolicy, refund_to_original_method), each method gives back at most what of the money that came in by it is still account credit; another method needs ledger.post where it is paid out. Money held on one order is refunded on that order with refundPayment instead. Returns the refund and the credit left.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundAccountCreditInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"The customer given the money back. Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"201":{"description":"Money a customer paid toward what they owe without naming one order, or given back from their account credit. Positive when received; negative for a refund of account credit, or for a reversal of a payment that never arrived (a bounced check). What is received is applied to their orders by payments of the On account method, and the rest is their account credit. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountPayment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/account-payments/{account_payment_id}/reverse":{"post":{"operationId":"reverseAccountPayment","summary":"Record that a payment on account did not arrive","tags":["Receivables"],"description":"Undoes money paid on account that turned out not to be money: a check that bounced, a card charged back. It goes back by the same method, at the location that took it. It takes back only that payment's money: what of it is still account credit, then what it paid on their orders, the latest applied first, which they owe again whatever their credit limit (applications lists those orders). Anything still not covered, because the money already went back to the customer, comes out of their other account credit, and beyond that they owe it on account (owed_on_account, in amount_due and due like an order); their next payment on account pays it first. It carries the payment's reference (the check number). Cash taken into a drawer comes back out of that drawer while it is open, and out of the safe it was counted into once it is closed. Needs payment.refund at the location that took it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReverseAccountPaymentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"account_payment_id","required":true,"description":"The payment on account that did not arrive."}],"responses":{"200":{"description":"Money a customer paid toward what they owe without naming one order, or given back from their account credit. Positive when received; negative for a refund of account credit, or for a reversal of a payment that never arrived (a bounced check). What is received is applied to their orders by payments of the On account method, and the rest is their account credit. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountPayment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/finance-charges/due":{"get":{"operationId":"getFinanceChargesDue","summary":"See what a month's finance charges would come to","tags":["Receivables"],"description":"Returns, for the month ending period_end (by default the latest month over at every location), each customer assessFinanceCharges would charge for paying late and what on: every order and every part of what they owe on account more than the grace days past due at the month's end, at the business's policy now, which it returns too. Customers charged for the month already, with less past due than the business charges on, or holding money paid on account are counted, not listed. A month is charged within a month of its end. Nothing is charged; it works with finance charges off too, to see what turning them on would charge. Requires the receivables.view permission.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"period_end","required":false,"description":"The last day of the month. Defaults to the latest month over at every location."}],"x-required-permission":"receivables.view","responses":{"200":{"description":"What assessFinanceCharges would charge for a month, at the business's policy now, and who it would leave out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceChargesDue"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/finance-charges":{"post":{"operationId":"assessFinanceCharges","summary":"Charge every customer who paid late for a month","tags":["Receivables"],"description":"Charges each customer getFinanceChargesDue lists for the month ending period_end, a month over at every location, and returns the charges. A business must turn finance charges on first (updatePolicy, finance_charges). Each charge is owed on account: it shows in listReceivables and on statements, falls due by the customer's payment terms, and a payment on account (takeAccountPayment) pays it before any order. A customer is charged once a month, in order: one charged for this month or a later one is left out, so running it again charges nobody twice. Needs credit.manage. Requires the credit.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssessFinanceChargesInput"}}}},"x-required-permission":"credit.manage","responses":{"201":{"description":"The month's finance charges, as made.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceChargesAssessed"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listFinanceCharges","summary":"List finance charges","tags":["Receivables"],"description":"Returns finance charges, newest first, each with what it was charged on, what is still owed on it and any waiver: one customer's (customer_id), one month's (period_end), or only those still owed (status owing). Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on. receivables.view held everywhere shows every one; filtered to one customer, all of theirs are shown.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's charges. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"period_end","required":false,"description":"Return only charges for the month ending this day."},{"schema":{"type":"string","enum":["owing","paid","waived"]},"in":"query","name":"status","required":false,"description":"Return only charges in this state."}],"responses":{"200":{"description":"One page of finance charges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceChargePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/finance-charges/{finance_charge_id}/waiver":{"post":{"operationId":"waiveFinanceCharge","summary":"Forgive what is still owed on a finance charge","tags":["Receivables"],"description":"Waives a finance charge: whatever is still owed on it comes off what the customer owes, and out of finance charge income. What they already paid on it stays paid. Returns the charge. Needs credit.manage. Requires the credit.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaiveFinanceChargeInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"finance_charge_number"},"in":"path","name":"finance_charge_id","required":true,"description":"The finance charge. Accepts the record's id or its finance charge number (as a number or a string of digits)."}],"x-required-permission":"credit.manage","responses":{"201":{"description":"A charge to one customer for paying late, for one month: what was past due at the month's end, charged at the business's rate for the days of the month it was past due (finance_charge_line), and at least its minimum. Owed on account like any debt with no order behind it (account_debt), it falls due by the customer's payment terms from the day it was charged. One per customer per month. Rows are only ever added; a waiver (finance_charge_waiver) forgives what is still owed on one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards":{"get":{"operationId":"listGiftCards","summary":"List gift cards","tags":["Gift Cards"],"description":"Returns gift cards, oldest first, with what each holds and what can be done with it next; never a code. Find a card by its number (card_number), the cards a customer bought (purchaser_customer_id), or those unused since a date with something on them (inactive_since), for breakage or the state's unclaimed property report. Someone who holds giftcard.manage also sees the last four of each code where they hold it, and can find cards by them (code_last4). To find a card from its code, use checkGiftCardBalance. Cards sold or given at a location are all listed for a key that holds sales.view there; elsewhere a key finds one card by its number (card_number), by the end of its code (code_last4), or one customer's (purchaser_customer_id).","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"in":"query","name":"card_number","required":false,"description":"Return only the card with this number."},{"schema":{"type":"string","pattern":"^[A-Za-z0-9]{4}$"},"in":"query","name":"code_last4","required":false,"description":"Return only cards whose code ends in these four characters, among those sold where this API key holds giftcard.manage. Needs giftcard.manage somewhere."},{"schema":{"type":"string","enum":["active","blocked","closed"]},"in":"query","name":"status","required":false,"description":"Return only cards in this state."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"purchaser_customer_id","required":false,"description":"Return only cards this customer bought. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"issued_location_id","required":false,"description":"Return only cards sold or given here. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"inactive_since","required":false,"description":"Return only cards holding something and unused since this date (last_activity_on on or before it)."}],"responses":{"200":{"description":"One page of gift cards, never with their codes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"sellGiftCard","summary":"Sell a gift card","tags":["Gift Cards"],"description":"Sells a new gift card holding amount, paid for with money: cash (into a drawer, cash_drawer_session_id), a check, a card or another method of the business's own. A card is not a sale of goods: no tax, revenue or commission until it is spent. Send code and pin for a card from the business's stock, or leave both out to have a 16-digit random code made: it is in the response this once, and never shown again. The business must sell gift cards (getPolicy, gift_cards_offered), and puts at most gift_card_max_load on one card a day. A mixed basket is a counter sale for the goods and a sale of the card. Returns the card, its first entry and its code.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SellGiftCardInput"}}}},"responses":{"201":{"description":"A gift card just sold, given or made to replace another.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardIssued"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/reloads":{"post":{"operationId":"reloadGiftCard","summary":"Put more on a gift card","tags":["Gift Cards"],"description":"Adds amount to a card the customer presents, by its code (and PIN), paid for with money as a sale is. The card must be active, the business must sell gift cards, and at most gift_card_max_load goes onto one card a day. Returns the card and the reload's entry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReloadGiftCardInput"}}}},"responses":{"200":{"description":"A gift card, and the entry that just changed what it holds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardChanged"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/lookup":{"post":{"operationId":"checkGiftCardBalance","summary":"See what a gift card holds, from its code","tags":["Gift Cards"],"description":"Finds the card a code (and PIN) opens and returns what it holds and everything that has moved it, never the code. A wrong code and a wrong PIN are refused alike; every wrong try counts against this API key, and ten in fifteen minutes lock it out of gift cards for fifteen minutes. The code travels in the body, never the URL. A card's number finds it too (getGiftCard), for whoever already knows it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckGiftCardBalanceInput"}}}},"responses":{"200":{"description":"A gift card the business sold or gave away: whoever holds its code can spend what it holds, here, as payment. Its code is never stored, only a hash the application cannot read; it is shown once, when the card is made. What it holds is kept by its ledger (gift_card_entry). schema.org: a GiftCard-like Offer of stored value; none of its terms apply. Its code is never shown.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCard"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/{gift_card_id}":{"get":{"operationId":"getGiftCard","summary":"Get a gift card","tags":["Gift Cards"],"description":"Returns a card by its id or number: what it holds, its state, and every entry that moved it (sold, spent on which order, refunded onto, voided, replaced, written off). Never its code.","parameters":[{"schema":{"type":"string","x-natural-key":"card_number"},"in":"path","name":"gift_card_id","required":true,"description":"The card, by its id or its number. Its number never spends it. Accepts the record's id or its card number (as a number or a string of digits)."}],"responses":{"200":{"description":"A gift card the business sold or gave away: whoever holds its code can spend what it holds, here, as payment. Its code is never stored, only a hash the application cannot read; it is shown once, when the card is made. What it holds is kept by its ledger (gift_card_entry). schema.org: a GiftCard-like Offer of stored value; none of its terms apply. Its code is never shown.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCard"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateGiftCard","summary":"Block or unblock a gift card, label it, or clear its PIN lock","tags":["Gift Cards"],"description":"Blocks a card reported lost or stolen, so nothing can be spent from it (anyone may), or unblocks it (needs giftcard.manage where it was sold); clears the lock wrong PINs put on it (the same); changes who it is for; and changes who bought it (the same), never once a sale or reload on it was paid by check, card or another method whose money could yet fail to arrive, since its buyer owes what was spent if it does. What a blocked card holds is still owed: replaceGiftCard moves it to a new card.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGiftCardInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"card_number"},"in":"path","name":"gift_card_id","required":true,"description":"The card, by its id or its number. Its number never spends it. Accepts the record's id or its card number (as a number or a string of digits)."}],"responses":{"200":{"description":"A gift card the business sold or gave away: whoever holds its code can spend what it holds, here, as payment. Its code is never stored, only a hash the application cannot read; it is shown once, when the card is made. What it holds is kept by its ledger (gift_card_entry). schema.org: a GiftCard-like Offer of stored value; none of its terms apply. Its code is never shown.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCard"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/promotional":{"post":{"operationId":"givePromotionalGiftCard","summary":"Give a gift card away","tags":["Gift Cards"],"description":"Gives a new card holding amount at the business's cost, charged to an expense account it names (ledger_account_id, for example Advertising): a raffle prize, a goodwill gesture. Needs giftcard.manage at location_id, and the business must sell gift cards. Send code and pin for a card from the stock, or leave both out to have a code made, returned this once. A card given away is never cashed out.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GivePromotionalGiftCardInput"}}}},"responses":{"201":{"description":"A gift card just sold, given or made to replace another.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardIssued"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/{gift_card_id}/void":{"post":{"operationId":"voidGiftCardSale","summary":"Take back a gift card sale or reload the same day","tags":["Gift Cards"],"description":"Undoes a card's latest sale or reload, on the business day it was made and while the card is unused since: the money goes back the way it came, where it was taken. Cash taken into a drawer comes back out of that drawer while it is open (leave cash_drawer_session_id out and it is found), and out of the safe once it is closed. A voided sale closes the card. Needs giftcard.manage and payment.refund where the sale was made. A card used since keeps its value: spend it, or refund what it paid for. Returns the card and the void's entry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoidGiftCardSaleInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"card_number"},"in":"path","name":"gift_card_id","required":true,"description":"The card, by its id or its number. Its number never spends it. Accepts the record's id or its card number (as a number or a string of digits)."}],"responses":{"200":{"description":"A gift card, and the entry that just changed what it holds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardChanged"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/{gift_card_id}/reverse":{"post":{"operationId":"reverseGiftCardSale","summary":"Take back a gift card sale or reload whose money never arrived","tags":["Gift Cards"],"description":"For a sale or reload paid by check, card or another method whose money never arrived (a bounced check, a chargeback), on any day: what the card (or the card that replaced it) still holds of it comes off and the card is blocked, or closed when nothing is left on it; what had already been spent is owed by the customer who bought the card, on account (their account credit pays it first; the rest is a receivable listReceivables lists and takeAccountPayment collects). Name the buyer (customer_id) when some was spent and the card names none (a card that names one is owed by them). Cash cannot fail to arrive: a cash sale is only voided, the same day. Needs giftcard.manage and payment.refund where the sale was made and where the reversal is recorded. It works whatever became of the card since: blocked, replaced, written off (what was written off of the sale is written back) or closed by an earlier reversal. Returns the card and the reversal's entry: amount came off the card, owed is what the buyer owes, written_back was written back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReverseGiftCardSaleInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"card_number"},"in":"path","name":"gift_card_id","required":true,"description":"The card, by its id or its number. Its number never spends it. Accepts the record's id or its card number (as a number or a string of digits)."}],"responses":{"200":{"description":"A gift card, and the entry that just changed what it holds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardChanged"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/{gift_card_id}/replace":{"post":{"operationId":"replaceGiftCard","summary":"Move what a lost or damaged gift card holds to a new card","tags":["Gift Cards"],"description":"Moves all a card holds, blocked or not, to a new card handed over at location_id, and closes the old one, so the old code spends nothing; money its old code paid on orders goes back onto the new card. A blocked card is replaced even when it holds nothing, so what it paid on orders can go back. For a card lost or stolen (block it first with updateGiftCard), damaged, or whose made-up code was lost. Nothing is posted: what is owed is the same. Needs giftcard.manage where the card was sold and at location_id. Send code and pin for a card from the stock, or leave both out to have a code made, returned this once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceGiftCardInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"card_number"},"in":"path","name":"gift_card_id","required":true,"description":"The card, by its id or its number. Its number never spends it. Accepts the record's id or its card number (as a number or a string of digits)."}],"responses":{"201":{"description":"A gift card just sold, given or made to replace another.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardIssued"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/cash-outs":{"post":{"operationId":"cashOutGiftCard","summary":"Pay out a small gift card balance as money","tags":["Gift Cards"],"description":"Pays out all a card the customer presents holds, as money (cash from a drawer, cash_drawer_session_id), where the business pays out small balances on request, as some states require: only a card holding less than gift_card_cash_out_below (getPolicy), never when that is not set, and never a card given away as a promotion. Paid out by check, it is a check the business writes, with its number in reference while the business requires check numbers. The card is closed. Needs giftcard.manage at the location. Returns the card, now empty and closed, and the cash-out's entry, whose amount (negative) is what was paid out.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashOutGiftCardInput"}}}},"responses":{"200":{"description":"A gift card, and the entry that just changed what it holds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardChanged"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/gift-cards/{gift_card_id}/write-off":{"post":{"operationId":"writeOffGiftCard","summary":"Write off what a gift card holds","tags":["Gift Cards"],"description":"Takes all a card holds off it and closes it: as breakage, into Gift Card Breakage income, only once it has gone unused for the business's dormancy period (getPolicy, gift_card_breakage_after_days; never when that is not set); or as unclaimed property owed to the state, into the liability account named until it is paid, only once it has gone unused for the state's dormancy period (gift_card_escheat_after_days). Needs giftcard.manage and ledger.post, both everywhere. listGiftCards with inactive_since finds dormant cards. Returns the card and the write-off's entry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteOffGiftCardInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"card_number"},"in":"path","name":"gift_card_id","required":true,"description":"The card, by its id or its number. Its number never spends it. Accepts the record's id or its card number (as a number or a string of digits)."}],"responses":{"200":{"description":"A gift card, and the entry that just changed what it holds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardChanged"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-readers":{"get":{"operationId":"listCardReaders","summary":"List card readers","tags":["Settings"],"description":"Returns the card readers registered to the business's card processor, oldest first, each with its store. Send a card charge to one with takeCardOnReader.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only the readers at this store. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"carried_by_staff_member_id","required":false,"description":"Return only the readers this person carries on the road."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"A page of card readers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardReaderPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"registerCardReader","summary":"Register a card reader","tags":["Settings"],"description":"Registers a card reader with the business's card processor, at one store, by the code it shows. What it takes is counted at that store; a delivery crew's reader belongs to their home store. A reader a technician or driver carries on the road (a cellular reader, or one on a phone's hotspot) names them in carried_by_staff_member_id, so their screens at a customer's door offer it. Requires the card.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterCardReaderInput"}}}},"x-required-permission":"card.manage","responses":{"201":{"description":"A card reader registered to the processor account: a countertop reader at a showroom, or a cellular one a delivery crew carries. Staff send a charge to it and the customer taps, dips or swipes there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardReader"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-readers/{card_reader_id}":{"patch":{"operationId":"updateCardReader","summary":"Rename or retire a card reader","tags":["Settings"],"description":"Changes a reader's name, who carries it on the road, or retires it (is_active false) so no charge is sent to it. Charges it took stay as they are. Requires the card.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardReaderInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"card_reader_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"card.manage","responses":{"200":{"description":"A card reader registered to the processor account: a countertop reader at a showroom, or a cellular one a delivery crew carries. Staff send a charge to it and the customer taps, dips or swipes there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardReader"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/reader":{"post":{"operationId":"takeCardOnReader","summary":"Take a card on a reader","tags":["Card Payments"],"description":"Sends a charge toward an order to a card reader, where the customer taps, inserts or swipes their card: at the counter, or on delivery day on the crew's reader. Omit amount to take what is left to pay. Returns at once, waiting_for_card: read it with getCardCharge until it is captured or declined. The card is only authorized at first; when it is, the payment is recorded for what the order can take then, and only that much is captured, so an order changed while the card was out is never overcharged. A declined card is a status, not an error. On a quote the card is its deposit: it must bring what is paid up to the business's min_deposit_percent, and the quote opens when the card is taken.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TakeCardOnReaderInput"}}}},"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales/counter/reader":{"post":{"operationId":"recordCounterSaleOnReader","summary":"Ring up a counter sale paid by card on the reader","tags":["Card Payments"],"description":"Rings up a sale paid for and taken away in one go, as recordCounterSale does, paid by a card on the reader at this counter for whatever the other payments (cash, a check) leave. Nothing is written yet: the sale is checked as it would be written (prices, tax, discounts this key may give, the other payments), the card is sent to the reader, and the answer is the card charge, waiting_for_card. Read it with getCardCharge every second or two. When the card is approved the sale is written, paid and handed over in one go, and the charge names its order (sales_order_id, order_number): then issue the receipt. Declined, called off (cancelCardCharge), or not presented within 15 minutes, nothing of the sale is written: try again with a new idempotency_key, or ring it up paid another way with recordCounterSale. The same idempotency_key sent again answers the same charge, so a retried request never sends a second card to the reader.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCounterSaleOnReaderInput"}}}},"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/link":{"post":{"operationId":"sendPaymentLink","summary":"Send the customer a link to pay by card","tags":["Card Payments"],"description":"Sends the order's customer a link to pay by card from home, by email or text (handed to the business's email or text service at once when one is connected, else queued for the sender), for a deposit or for what is left to pay. When the customer opens it they pay on the card processor's page; until then nothing is held on the order. The card is only authorized at first, and the payment is recorded for what the order can take when the customer pays. The link works for the business's payment_link_valid_days. On a quote it is the deposit: it must cover the business's min_deposit_percent, and the quote opens when the customer pays. Their consent decides whether the message goes: one held back says so (message_status suppressed). Returns the charge, awaiting_customer, with the link itself.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendPaymentLinkInput"}}}},"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges":{"get":{"operationId":"listCardCharges","summary":"List card charges","tags":["Card Payments"],"description":"Returns card charges through the processor, oldest first: on readers, by link and from cards on file, with their refunds and chargebacks. Charges taken at a location are all shown to a key that holds sales.view there; elsewhere a key sees only charges on orders its staff member is credited on, those it started, or every charge on one order (sales_order_id).","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only charges on this order. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["waiting_for_card","awaiting_customer","processing","capturing","captured","declined","canceled","expired","capture_failed"]},"in":"query","name":"status","required":false,"description":"Return only charges in this status."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only charges taken at this store. Accepts the record's id or its branch code."}],"responses":{"200":{"description":"A page of card charges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardChargePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/{card_charge_id}":{"get":{"operationId":"getCardCharge","summary":"Get a card charge","tags":["Card Payments"],"description":"Returns one card charge: where it stands, the card (brand and last four digits), what was asked for, authorized and captured, the payment it recorded, and its refunds and chargebacks. A charge still going is read from the processor first, so this is how to wait for a customer at a reader: read it every second or two until it is captured, declined or canceled.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"card_charge_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/{card_charge_id}/cancel":{"post":{"operationId":"cancelCardCharge","summary":"Call off a card charge","tags":["Card Payments"],"description":"Calls off a charge that has taken no money: clears the reader, or stops a payment link working. Refused once the card has been taken: give it back with refundCard.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelCardChargeInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"card_charge_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/{card_charge_id}/refunds":{"post":{"operationId":"refundCard","summary":"Give money back to the card","tags":["Card Payments"],"description":"Gives money back to the card a captured charge took, through the processor. Needs payment.refund where the charge was taken. A refund gives back only money the order holds for the customer, never leaving them owing: to refund for goods coming back, record their return first (returnSalesOrderGoods). The refund is recorded on the order before the processor is asked; if the processor cannot pay it, it is undone and the order holds the money again. Returns the charge with its refunds.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCardInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"card_charge_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-processor":{"get":{"operationId":"getCardProcessor","summary":"Get the card processor","tags":["Card Payments"],"description":"Returns the business's card processor: which account and mode, its payment method, what its clearing account holds, and what needs a person: news it could not act on, a capture or refund not yet confirmed, a chargeback to answer in the processor's dashboard. connected is false when none is connected.","responses":{"200":{"description":"The business's card processor: its account, mode, clearing account and what needs attention. Connected by the business's provider setup, not through the API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardProcessor"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateCardProcessor","summary":"Choose where the card processor's payouts post","tags":["Card Payments"],"description":"Chooses the accounts the processor's payouts post to: the bank account they land in (deposit_account_id, an asset), the expense its fees post to (fee_account_id), and where money it carries that was not taken here posts (other_activity_account_id: another sales channel on the same account, an adjustment). Applies to payouts recorded from then on. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardProcessorInput"}}}},"x-required-permission":"ledger.post","responses":{"200":{"description":"The business's card processor: its account, mode, clearing account and what needs attention. Connected by the business's provider setup, not through the API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardProcessor"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-processor/sync":{"post":{"operationId":"syncCardProcessor","summary":"Catch up with the card processor","tags":["Card Payments"],"description":"Brings every open charge and pending refund in line with the processor, and reads back the processor's news since the last sync, acting on anything missed or failed. Safe to run at any time and as often as wanted; nothing is done twice. Requires the card.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncCardProcessorInput"}}}},"x-required-permission":"card.manage","responses":{"200":{"description":"What a sync with the card processor did.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardProcessorSync"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/saved-cards":{"get":{"operationId":"listSavedCards","summary":"List a customer's cards on file","tags":["Card Payments"],"description":"Returns the cards a customer keeps on file, oldest first, each with what they agreed it may be charged for. A card still being set up is read from the processor first, so this is how to wait while the customer presents it at the reader.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"A page of cards on file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedCardPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"keepCardOnFile","summary":"Keep a customer's card on file for their plan","tags":["Card Payments"],"description":"Asks the reader for the customer's card, to keep on file for their care plan or auto-ship, under the agreement sent: one subscription, up to amount_limit a cycle, until they cancel. The terms are written by the system (terms_text) and never change. Returns at once, pending: read listSavedCards until the card is active or failed. Then each cycle's order can be charged with chargeCardOnFile.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeepCardOnFileInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"A customer's card kept on file at the card processor, set up on a reader in person, so a care plan or auto-ship can be charged each cycle without the customer there. It may be charged only as its card_agreement allows. The processor holds the card; nothing here holds its number. Its status moves only through the database.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedCard"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/saved-cards/{saved_card_id}/remove":{"post":{"operationId":"removeSavedCard","summary":"Take a card off file","tags":["Card Payments"],"description":"Takes a card off file, here first so it can never be charged again, then at the processor; ends every agreement in force for it. A card still being set up is called off.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveSavedCardInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"saved_card_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A customer's card kept on file at the card processor, set up on a reader in person, so a care plan or auto-ship can be charged each cycle without the customer there. It may be charged only as its card_agreement allows. The processor holds the card; nothing here holds its number. Its status moves only through the database.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedCard"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-agreements/{card_agreement_id}/end":{"post":{"operationId":"endCardAgreement","summary":"End a card agreement","tags":["Card Payments"],"description":"Ends what a customer agreed their card on file may be charged for: when they cancel, or the business stops. Nothing more is charged under it; the card stays on file until removed. Returns the card.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndCardAgreementInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"card_agreement_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A customer's card kept on file at the card processor, set up on a reader in person, so a care plan or auto-ship can be charged each cycle without the customer there. It may be charged only as its card_agreement allows. The processor holds the card; nothing here holds its number. Its status moves only through the database.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedCard"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/saved-card":{"post":{"operationId":"chargeCardOnFile","summary":"Charge a subscription order to the card on file","tags":["Card Payments"],"description":"Charges what is left to pay on an order of a subscription (a care plan visit, an auto-ship cycle) to the customer's card on file, under the card agreement in force for that subscription, at most its limit. The agreement is weighed again when the card is authorized, and only what the order can take is captured. Returns the charge, captured or declined: a declined card is a status, not an error; then send a payment link.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeCardOnFileInput"}}}},"responses":{"200":{"description":"One attempt to take a card toward an order through the card processor: on a reader, by a payment link, or from a card on file; or, at the counter, for a sale written only once the card is approved. The card is only authorized at first; the payment is recorded for what the order can take when the card is authorized, and only that much is captured. Its status moves only as the processor reports, through the database; rows are never deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCharge"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-charges/due":{"post":{"operationId":"chargeDueCardsOnFile","summary":"Charge every subscription order owing to its card on file","tags":["Card Payments"],"description":"Charges each open order of a subscription with a card agreement in force that has something left to pay, oldest first, at most 50 charged a call: one try per order per day, so a declined card is not tried again until tomorrow. Orders over the agreement's limit are skipped with why. Run it daily, after the subscription orders are made (makeDueSubscriptionOrders). Requires the card.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeDueCardsOnFileInput"}}}},"x-required-permission":"card.manage","responses":{"200":{"description":"What charging the cards on file for every subscription order owing did.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DueCardCharges"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"provider_unavailable: An outside service the request needs (the card processor) could not be reached or failed. Nothing was lost: send the same request again, unchanged, after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-payouts":{"get":{"operationId":"listCardPayouts","summary":"List the card processor's payouts","tags":["Card Payments"],"description":"Returns the processor's payouts to the bank, oldest first: what each carried of the card money taken here, its fees, anything else it carried, and what reached the bank. Match each to the bank statement by amount and arrival_date. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"arrived_from","required":false,"description":"Return only payouts arriving on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"arrived_to","required":false,"description":"Return only payouts arriving on or before this date."}],"x-required-permission":"cost.view","responses":{"200":{"description":"A page of the card processor's payouts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardPayoutPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/card-payouts/{card_payout_id}":{"get":{"operationId":"getCardPayout","summary":"Get a card processor payout","tags":["Card Payments"],"description":"Returns one payout with every charge, refund and chargeback it carried, each with its order and store, and the journal entry it posted. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"card_payout_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"Money the card processor paid out to the business's bank: what it carried of the card money recorded here, less the processor's fees, and anything else it moved on the same account. A ledger written only by the database, as the processor reports each payout paid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardPayout"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/receipts":{"post":{"operationId":"issueReceipt","summary":"Issue a receipt for what happened on an order","tags":["Receipts"],"description":"Issues a numbered receipt for everything on the order that no receipt covers yet: goods handed over or returned, payments and refunds. A second receipt on the same order shows only what came after the first. Send cash_tendered when the customer paid cash, to print their change. The receipt is built from the records and never changes; print it with printReceipt (again, or as a gift receipt) and email it with emailReceipt. Refused when there is nothing new: print the last one again instead. Payments on account and gift card sales issue their own receipt.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueReceiptInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"The order. Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"201":{"description":"A receipt the business issued: for what happened on an order since its last receipt (a sale, a deposit, a payment, a return or a refund), for a payment on account, or for a gift card sold or reloaded. Numbered with no gaps, and built by the database from the records at the moment it was issued; what it says (content) never changes, so a reprint shows the same facts. Rows are only ever added. schema.org: an Invoice-like record of an Order's payment; none of its terms are used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Receipt"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/receipts/{receipt_id}":{"get":{"operationId":"getReceipt","summary":"Get a receipt, as a record or as it prints","tags":["Receipts"],"description":"Returns a receipt with what it covers and each time it was printed or emailed. With format text or html, rendered holds it as it prints; that records no print (printReceipt does).","parameters":[{"schema":{"default":"json","type":"string","enum":["json","text","html"]},"in":"query","name":"format","required":false,"description":"json (the default): the record only, rendered null. text or html: the record with rendered filled in, as it would print. Reading it this way records no print."},{"schema":{"type":"string","x-natural-key":"receipt_number"},"in":"path","name":"receipt_id","required":true,"description":"The receipt. Accepts the record's id or its receipt number (as a number or a string of digits)."}],"responses":{"200":{"description":"A receipt the business issued: for what happened on an order since its last receipt (a sale, a deposit, a payment, a return or a refund), for a payment on account, or for a gift card sold or reloaded. Numbered with no gaps, and built by the database from the records at the moment it was issued; what it says (content) never changes, so a reprint shows the same facts. Rows are only ever added. schema.org: an Invoice-like record of an Order's payment; none of its terms are used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Receipt"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/receipts":{"get":{"operationId":"listReceipts","summary":"List receipts","tags":["Receipts"],"description":"Returns receipts, newest first, filtered by order, customer, location, kind or date: one order's receipts is sales_order_id. Receipts issued at a location are all shown to a key that holds sales.view there; elsewhere a key sees those on orders its staff member is credited on, those it issued, and every receipt of one order or customer it names.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only this order's receipts. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only receipts for this customer. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"account_payment_id","required":false,"description":"Return only this payment on account's receipt."},{"schema":{"type":"string","x-natural-key":"card_number"},"in":"query","name":"gift_card_id","required":false,"description":"Return only receipts for sales and reloads of this card. Accepts the record's id or its card number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only receipts issued at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["sale","deposit","payment","return","refund","account_payment","gift_card"]},"in":"query","name":"kind","required":false,"description":"Return only receipts of this kind."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"issued_from","required":false,"description":"Return only receipts issued on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"issued_to","required":false,"description":"Return only receipts issued on or before this date."}],"responses":{"200":{"description":"One page of receipts, newest first, each without its rendering.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/receipts/{receipt_id}/prints":{"post":{"operationId":"printReceipt","summary":"Print a receipt, again, or as a gift receipt","tags":["Receipts"],"description":"Records a print of the receipt and returns what to print. Every print after the first is marked REPRINT and shows the same facts. A gift receipt (gift_receipt) shows the goods without their prices, tenders or totals, with the return policy, for the person the goods are for.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrintReceiptInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"receipt_number"},"in":"path","name":"receipt_id","required":true,"description":"The receipt. Accepts the record's id or its receipt number (as a number or a string of digits)."}],"responses":{"201":{"description":"A receipt just printed, and what to print.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrintedReceipt"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/receipts/{receipt_id}/emails":{"post":{"operationId":"emailReceipt","summary":"Email a receipt to the customer","tags":["Receipts"],"description":"Writes the receipt as a service email to its customer's primary email address, or the contact point of theirs named. It goes out only where their consent allows: when it does not, the email is recorded as suppressed with the reason (deliveries), and nothing is sent. A receipt goes only to its own customer (customer_id): one issued with no customer can be emailed once its order names one, and one whose order has since moved to another customer is emailed to no one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailReceiptInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"receipt_number"},"in":"path","name":"receipt_id","required":true,"description":"The receipt. Accepts the record's id or its receipt number (as a number or a string of digits)."}],"responses":{"201":{"description":"A receipt the business issued: for what happened on an order since its last receipt (a sale, a deposit, a payment, a return or a refund), for a payment on account, or for a gift card sold or reloaded. Numbered with no gaps, and built by the database from the records at the moment it was issued; what it says (content) never changes, so a reprint shows the same facts. Rows are only ever added. schema.org: an Invoice-like record of an Order's payment; none of its terms are used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Receipt"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customers/{customer_id}/statements":{"post":{"operationId":"issueStatement","summary":"Issue a customer's statement for a period","tags":["Receivables"],"description":"Issues a statement of the customer's account through period_end, a day already over at every location: what they owed as it began (the previous statement's closing balance), each charge, return, payment and refund in it with the running balance, and what they owed at its end with what is overdue and by how long. It starts the day after their previous statement, or for a first statement over the month ending on period_end unless period_start is sent. What moved after the period up to the issue is shown after it, and amount_due_at_issue is what the statement asks for: a payment made after the period is never asked for again. A statement is never issued again: anything earlier corrected since shows on the next as an adjustment. Email it with emailStatement, or read it as it prints with getStatement. Needs credit.manage. Requires the credit.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueStatementInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"path","name":"customer_id","required":true,"description":"The customer. Accepts the record's id or its customer number (as a number or a string of digits)."}],"x-required-permission":"credit.manage","responses":{"201":{"description":"A statement of one customer's account for a period: what they owed at its start, what was handed over, returned, paid and given back in it, and what they owed at its end, with what is overdue and by how long. Built by the database from dated records, never reissued: each opens on the previous one's closing balance, and anything earlier that changed since shows on the next one as an adjustment. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Statement"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/statements":{"post":{"operationId":"issueStatements","summary":"Issue the period's statements for every customer who owes","tags":["Receivables"],"description":"Issues a statement through period_end for each customer who owes the business at its end and still owes now (only_owing, the default; paid_up_since counts those left out because they have paid since), or also for each with a balance either way or anything that moved in the period. Customers who already have a statement through period_end or later are left out. Each statement is as issueStatement makes it. Needs credit.manage. Requires the credit.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueStatementsInput"}}}},"x-required-permission":"credit.manage","responses":{"201":{"description":"The statements a batch issued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssuedStatements"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listStatements","summary":"List statements","tags":["Receivables"],"description":"Returns statements, newest first: one customer's (customer_id), one period's (period_end), or those still showing something owed or overdue. A statement is a customer's whole account, wherever they bought: a key that holds sales.view everywhere sees every statement; any other key sees one customer's (customer_id) and those it issued.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's statements. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"period_end","required":false,"description":"Return only statements ending on this day."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"owing","required":false,"description":"true: only statements with an amount due. false: only those with none."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"overdue","required":false,"description":"true: only statements with something overdue. false: only those with nothing overdue."}],"responses":{"200":{"description":"One page of statements, newest first, each without its rendering.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatementPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/statements/{customer_statement_id}":{"get":{"operationId":"getStatement","summary":"Get a statement, as a record or as it prints","tags":["Receivables"],"description":"Returns a statement with every line on it. With format text or html, rendered holds it as it prints.","parameters":[{"schema":{"default":"json","type":"string","enum":["json","text","html"]},"in":"query","name":"format","required":false,"description":"json (the default): the record only, rendered null. text or html: the record with rendered filled in, as it would print. Reading it this way records no print."},{"schema":{"type":"string","x-natural-key":"statement_number"},"in":"path","name":"customer_statement_id","required":true,"description":"The statement. Accepts the record's id or its statement number (as a number or a string of digits)."}],"responses":{"200":{"description":"A statement of one customer's account for a period: what they owed at its start, what was handed over, returned, paid and given back in it, and what they owed at its end, with what is overdue and by how long. Built by the database from dated records, never reissued: each opens on the previous one's closing balance, and anything earlier that changed since shows on the next one as an adjustment. Rows are only ever added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Statement"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/statements/{customer_statement_id}/emails":{"post":{"operationId":"emailStatement","summary":"Email a statement to its customer","tags":["Receivables"],"description":"Writes the statement as a service email to the customer's primary email address, or the contact point named. It goes out only where their consent allows: when it does not, the email is recorded as suppressed with the reason, and nothing is sent.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatementInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"statement_number"},"in":"path","name":"customer_statement_id","required":true,"description":"The statement. Accepts the record's id or its statement number (as a number or a string of digits)."}],"responses":{"201":{"description":"A statement just emailed, and what became of the email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailedStatement"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-lenders":{"get":{"operationId":"listFinancingLenders","summary":"List the consumer lenders customers can finance purchases with","tags":["Financing"],"description":"Returns each consumer lender (not floor-plan lenders: listFloorPlanLenders) with its promotions (term, rate, dealer fee, minimum), its payment method, how soon it pays out, and what it owes now (unfunded, overdue). Start here to sell on financing: choose a promotion the amount qualifies for, getFinancingEstimates for the monthly payment, then applyForFinancing.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of lenders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingLenderPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createFinancingLender","summary":"Add a consumer lender customers can finance purchases with","tags":["Financing"],"description":"Adds a consumer lender (one that lends to customers; a lender against the business's own stock is createFloorPlanLender) with the account its payouts land in (an asset account of the business's own, such as its bank: createLedgerAccount first if needed), how many days it usually takes to pay out, whether it withholds its dealer fee or bills it, and the promotions it offers. The lender gets its own payment method, named after it: a payment by it is owed by the lender until it pays out. Requires the financing.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFinancingLenderInput"}}}},"x-required-permission":"financing.manage","responses":{"201":{"description":"A consumer lender: a finance company that lends to the business's customers so they can buy, for example a card issuer's promotional plans or an installment lender. Not a floor-plan lender, which lends to the business itself against its stock (floor_plan_lender). Each has its own payment method for taking a financed payment, the promotions it offers, and the terms it pays out on. All of it is the business's own setup.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingLender"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-lenders/{financing_lender_id}":{"patch":{"operationId":"updateFinancingLender","summary":"Change a lender's terms","tags":["Financing"],"description":"Changes the fields sent. Its payment method follows its name and is_active. A new deposit account or fee settlement applies to payouts recorded from then on. Set is_active false to stop selling on it: what it already financed is still refunded, reversed and paid out. Requires the financing.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFinancingLenderInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_lender_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"financing.manage","responses":{"200":{"description":"A consumer lender: a finance company that lends to the business's customers so they can buy, for example a card issuer's promotional plans or an installment lender. Not a floor-plan lender, which lends to the business itself against its stock (floor_plan_lender). Each has its own payment method for taking a financed payment, the promotions it offers, and the terms it pays out on. All of it is the business's own setup.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingLender"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-lenders/{financing_lender_id}/promotions":{"post":{"operationId":"addFinancingPromotion","summary":"Add a promotion a lender offers","tags":["Financing"],"description":"Adds a financing plan: its term in months, the customer's yearly rate (0 for no interest), the dealer fee the lender charges the business (percent of what is financed, plus any fixed amount) and the least it finances. Returns the lender with all its promotions. Requires the financing.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddFinancingPromotionInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_lender_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"financing.manage","responses":{"201":{"description":"A consumer lender: a finance company that lends to the business's customers so they can buy, for example a card issuer's promotional plans or an installment lender. Not a floor-plan lender, which lends to the business itself against its stock (floor_plan_lender). Each has its own payment method for taking a financed payment, the promotions it offers, and the terms it pays out on. All of it is the business's own setup.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingLender"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-promotions/{financing_promotion_id}":{"patch":{"operationId":"updateFinancingPromotion","summary":"Change a lender's promotion","tags":["Financing"],"description":"Changes the fields sent. Applications already made keep the terms they were made on; the change applies to new ones. Set is_active false when the lender stops offering it. Requires the financing.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFinancingPromotionInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_promotion_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"financing.manage","responses":{"200":{"description":"One financing plan a lender offers the business's customers, for example \"12 months, no interest\" or \"84 months at 9.99%\", with what it costs the business. An application is made on one; its terms are copied onto the application, so changing a promotion changes only applications made after.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingPromotion"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-estimates":{"get":{"operationId":"getFinancingEstimates","summary":"Estimate monthly payments on each promotion","tags":["Financing"],"description":"For an amount, or what is left to pay on an order (a quote's whole total), gives every promotion on offer with an ESTIMATED monthly payment: the amount over the term at no interest, else the standard amortized payment, rounded up to the cent. Present it as an estimate: the lender's terms decide what the customer pays. qualifies is false below a promotion's minimum. dealer_fee_estimate is the business's cost, not the customer's.","parameters":[{"schema":{"type":"string","pattern":"^-?\\d{1,12}(\\.\\d{1,2})?$"},"in":"query","name":"amount","required":false,"description":"The amount to finance. A decimal string with up to 2 decimal places, for example \"12.50\"."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Estimate on what is left to pay on this order. Send this or amount. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"financing_lender_id","required":false,"description":"Only this lender's promotions."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"financing_promotion_id","required":false,"description":"Only this promotion."}],"responses":{"200":{"description":"Monthly payment estimates for the promotions on offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingEstimates"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/sales-orders/{sales_order_id}/financing-applications":{"post":{"operationId":"applyForFinancing","summary":"Record a customer's financing application","tags":["Financing"],"description":"Records that the order's customer applied to a lender on one of its promotions (listFinancingLenders), as submitted in the lender's own portal; send decision when the answer came at once. The order must be a quote or open order with a customer. Once approved, take the financed payment with takePayment by the lender's payment method: the customer's balance is paid, and the lender owes the business until it pays out (recordFinancingFunding). Commission waiting for payment in full is earned then.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyForFinancingInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"path","name":"sales_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"201":{"description":"A customer's application to a lender to finance an order on one of its promotions, and the lender's answer. Staff record it as the lender gives it; no lender is called. An approved application is what a payment by the lender's method is taken against, up to the amount approved and while the approval lasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingApplication"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-applications":{"get":{"operationId":"listFinancingApplications","summary":"List financing applications","tags":["Financing"],"description":"Returns applications newest first, with what was financed, paid out and is still owed by the lender, and whether that is late. unfunded=true lists financed sales a lender has not paid out yet; overdue=true only those past the lender's usual funding_days; expiring_within_days lists approvals with room left that run out soon. Applications on orders made at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on, or those on one order (sales_order_id).","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"financing_lender_id","required":false,"description":"Only this lender's."},{"schema":{"type":"string","enum":["applied","approved","declined","cancelled","funded"]},"in":"query","name":"status","required":false,"description":"Only applications in this status."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Only this order's. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Only this customer's. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"unfunded","required":false,"description":"true: only those the lender still owes on (or is owed back on); false: only those it does not."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"overdue","required":false,"description":"true: only those the lender owes on past its usual funding_days."},{"schema":{"type":"integer","minimum":0,"maximum":365},"in":"query","name":"expiring_within_days","required":false,"description":"Only approvals with room left whose last day is within this many days from today."}],"responses":{"200":{"description":"One page of financing applications.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingApplicationPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-applications/{financing_application_id}":{"get":{"operationId":"getFinancingApplication","summary":"Get a financing application","tags":["Financing"],"description":"Returns the application with the lender's answer, what can still be taken through it (available), what was financed and paid out, a monthly payment estimate, and what can be done next.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_application_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A customer's application to a lender to finance an order on one of its promotions, and the lender's answer. Staff record it as the lender gives it; no lender is called. An approved application is what a payment by the lender's method is taken against, up to the amount approved and while the approval lasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingApplication"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateFinancingApplication","summary":"Change an application's promotion or amount","tags":["Financing"],"description":"Changes the fields sent. The promotion can move to another of the same lender's until money has been taken through the application; its terms are copied again. For the lender's answer use recordFinancingDecision.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFinancingApplicationInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_application_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A customer's application to a lender to finance an order on one of its promotions, and the lender's answer. Staff record it as the lender gives it; no lender is called. An approved application is what a payment by the lender's method is taken against, up to the amount approved and while the approval lasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingApplication"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-applications/{financing_application_id}/decision":{"post":{"operationId":"recordFinancingDecision","summary":"Record the lender's answer on an application","tags":["Financing"],"description":"approved: the lender agreed, up to amount_approved (default: the amount asked for) until approval_expires_on; send again to record a changed amount or expiry. declined: the lender refused or withdrew its approval. Whatever was taken through it that the lender has not paid out is reversed, as for a bounced check: the customer owes it again (needs payment.refund at the order's location). Once the lender has paid out, give money back by refunding to its method instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordFinancingDecisionInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_application_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A customer's application to a lender to finance an order on one of its promotions, and the lender's answer. Staff record it as the lender gives it; no lender is called. An approved application is what a payment by the lender's method is taken against, up to the amount approved and while the approval lasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingApplication"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-applications/{financing_application_id}/cancel":{"post":{"operationId":"cancelFinancingApplication","summary":"Drop a financing application","tags":["Financing"],"description":"The customer or the business will not use the application (paying another way, or applying elsewhere). Whatever was taken through it is reversed, and the customer owes it again or has that much less on deposit; needs payment.refund at the order's location when money was taken. Not once the lender has paid out: refund to the lender's method instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelFinancingApplicationInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_application_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A customer's application to a lender to finance an order on one of its promotions, and the lender's answer. Staff record it as the lender gives it; no lender is called. An approved application is what a payment by the lender's method is taken against, up to the amount approved and while the approval lasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingApplication"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-lenders/{financing_lender_id}/fundings":{"post":{"operationId":"recordFinancingFunding","summary":"Record a lender's payout","tags":["Financing"],"description":"Records one payout from the lender's statement, a row per sale (listFinancingApplications, unfunded true). Each row clears what the lender owed on the sale; the money goes to the lender's deposit account and the dealer fee to Financing Fees (or, for a lender that bills its fee, to Lender Fees Payable). Leave dealer_fee out for the promotion's fee, or send amount_received as on the statement. A negative amount is the lender taking money back. Returns the rows and the total to match to the bank. Requires the financing.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordFinancingFundingInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"financing_lender_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"financing.manage","responses":{"201":{"description":"A lender's payout, as recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingFundingBatch"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-fundings":{"get":{"operationId":"listFinancingFundings","summary":"List lender payouts","tags":["Financing"],"description":"Returns payouts and amounts lenders took back, newest first, with the dealer fee on each. Filter by lender, application, order or date range: one day's rows for a lender are what its bank deposit should equal. Payouts on orders made at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on, or those on one order (sales_order_id).","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"financing_lender_id","required":false,"description":"Only this lender's."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"financing_application_id","required":false,"description":"Only payouts on this application."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Only payouts on this order. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"funded_from","required":false,"description":"Only payouts dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"funded_to","required":false,"description":"Only payouts dated on or before this date."}],"responses":{"200":{"description":"One page of lender payouts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingFundingPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/financing-report":{"get":{"operationId":"getFinancingReport","summary":"See what lenders owe and what financing cost","tags":["Financing"],"description":"One row per lender: what it owes now and how much of that is past its usual funding_days (and how late the oldest is), what was financed and paid out in the period with the dealer fees on it, and how many approvals with room left run out within expiring_within_days. The period defaults to this month to date. It covers every store's sales, so it needs sales.view for the whole business. Requires the sales.view permission.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"First day of the period. Defaults to the first of this month."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"Last day of the period. Defaults to today."},{"schema":{"default":7,"type":"integer","minimum":0,"maximum":365},"in":"query","name":"expiring_within_days","required":false,"description":"How far ahead to count approvals about to run out. Defaults to 7."}],"x-required-permission":"sales.view","responses":{"200":{"description":"What lenders owe, how late they are, and what financing cost.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancingReport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders":{"get":{"operationId":"listPurchaseOrders","summary":"List purchase orders","tags":["Purchasing"],"description":"Returns purchase orders, oldest first (newest_first=true for the latest first). Filter by status, vendor, location or an item on its lines; search takes a purchase order number.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["draft","ordered","closed","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only purchase orders in this status."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"vendor_id","required":false,"description":"Return only purchase orders to this vendor."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only purchase orders delivering to this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only purchase orders with a line for this item. Accepts the record's id or its SKU."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"newest_first","required":false,"description":"Send true to page from the latest purchase order back; the cursor then moves back."}],"responses":{"200":{"description":"One page of purchase orders and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrderPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createPurchaseOrder","summary":"Raise a purchase order","tags":["Purchasing"],"description":"Starts a purchase order to one vendor for delivery to one location, as a draft, with its lines. Costs and vendor codes default to those on record for the vendor (setItemVendor). Send it with sendPurchaseOrder.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePurchaseOrderInput"}}}},"responses":{"201":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}":{"get":{"operationId":"getPurchaseOrder","summary":"Get a purchase order","tags":["Purchasing"],"description":"Returns one purchase order with its lines, what has arrived and what is still expected, every receipt and return, and next: what can be done with it now. Costs need cost.view.","parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updatePurchaseOrder","summary":"Update a purchase order","tags":["Purchasing"],"description":"Changes the fields sent: when the goods are expected, notes, where they are delivered. A new expected_on is the vendor's date for everything still to come: every such line moves to it, each move kept with date_change_note in the line's date_changes. The vendor can change only on a draft with no lines.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePurchaseOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/lines":{"post":{"operationId":"addPurchaseOrderLines","summary":"Add lines to a purchase order","tags":["Purchasing"],"description":"Adds items to a draft or ordered purchase order. Name sales_order_line_id to buy for a customer's order (a special order).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPurchaseOrderLinesInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/lines/{purchase_order_line_id}":{"patch":{"operationId":"updatePurchaseOrderLine","summary":"Change a line on a purchase order","tags":["Purchasing"],"description":"Changes the fields sent on one line. Quantity 0 drops it. Once goods have arrived on a line its cost is fixed and its quantity cannot go below what arrived. expected_on records the vendor's new date for what is still to come, kept with date_change_note in the line's date_changes; the customers waiting on the line are in its waiting_customers, to be told.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePurchaseOrderLineInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"purchase_order_line_id","required":true,"description":"The line, from the purchase order's lines."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/send":{"post":{"operationId":"sendPurchaseOrder","summary":"Mark a purchase order sent","tags":["Purchasing"],"description":"Records that the purchase order has gone to the vendor. Goods can then be received against it.","parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/close":{"post":{"operationId":"closePurchaseOrder","summary":"Close a purchase order","tags":["Purchasing"],"description":"Records that nothing more will come on an ordered purchase order. The system closes it by itself when everything has arrived. Goods can still be sent back; sending them back on an order that had arrived in full reopens it for their replacement.","parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/cancel":{"post":{"operationId":"cancelPurchaseOrder","summary":"Cancel a purchase order","tags":["Purchasing"],"description":"Withdraws a purchase order nothing has arrived on. Final.","parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/receipts":{"post":{"operationId":"receivePurchaseOrder","summary":"Receive goods on a purchase order","tags":["Purchasing"],"description":"Records goods that arrived: everything still expected, or the lines and quantities sent. Stock goes on hand at the line's cost, or at unit_cost when the vendor's invoice differs. A line ordered by the pack can be received in packs, which go into stock at exactly pack_cost each. For a serial-tracked item send the serial number of each unit; the system records each unit, and a unit on a line bought for a customer's order (sales_order_line_id) is promised to that order line if it is still waiting for one. For an item tracked by lot, name the lot of what arrived (lot_code, and expires_on for a new one): a delivery of two lots is two lines. A unit that arrived built otherwise than its line asked is recorded with the options sent (options), and is not promised to a customer who bought another build. Naming another cost, or receiving at a location other than the order's, needs purchase.manage there (and at the order's location). The order closes by itself when everything has arrived.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceivePurchaseOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/returns":{"post":{"operationId":"returnToVendor","summary":"Send goods back to the vendor","tags":["Purchasing"],"description":"Records goods going back to the vendor from a purchase order they arrived on. They leave stock where they are, and what is owed to the vendor falls by what they were received at. A serial-tracked unit is named by its id, and is credited at what that unit was received at; goods tracked by lot name their lot. Sending goods back on an order that had arrived in full reopens it, so the vendor's replacement can be received; close it if the vendor credits them instead. Record the vendor's return authorization (vendor_rma_number) and what it said it will credit (vendor_credit_expected) for whoever enters its credit memo in the accounting system: what is owed here comes down by what the goods were received at either way. Goods held for the vendor leave stock now. Goods already written off as a loss (damaged in freight) name that loss (written_off_stock_movement_id; a written-off unit's is found), so they leave stock once. Needs purchase.manage where the goods leave from: location_id, or where they are (a unit's own location), and the order's location when location_id is left out.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnToVendorInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"200":{"description":"What the tenant has asked one vendor to supply to one location. Goods are received against its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/landed-costs":{"get":{"operationId":"listLandedCosts","summary":"List landed costs","tags":["Purchasing"],"description":"Returns freight, cranes, prep and other costs added to goods after they arrived, oldest first, each with every arrival's share and where it went: onto stock on hand, or to cost of goods sold. Filter by purchase order, item or serialized unit. Needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"query","name":"purchase_order_id","required":false,"description":"Return only landed costs on this purchase order. Accepts the record's id or its purchase order number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Return only landed costs with a share for this item. Accepts the record's id or its SKU."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"serialized_unit_id","required":false,"description":"Return only landed costs with a share for this unit."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of landed costs and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LandedCostPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/purchase-orders/{purchase_order_id}/landed-costs":{"post":{"operationId":"addLandedCost","summary":"Add freight or prep to what goods cost","tags":["Purchasing"],"description":"Adds a cost paid after goods arrived on a purchase order (a carrier's freight bill, a crane, prep) to what they cost, shared among the order's arrivals by what each cost (basis value) or how many each brought (basis quantity): all of them, the receipts named, or the serialized units named. The share of goods still on hand raises their cost; the share of goods already sold or gone goes to cost of goods sold today. It is credited to Inventory Received Not Billed, cleared when the bill is entered in the accounting system, or to ledger_account_id, which needs ledger.post. Needs purchase.manage at the order's location. Its split and each arrival's share are shown only to a key that holds cost.view.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLandedCostInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"purchase_order_number"},"in":"path","name":"purchase_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its purchase order number (as a number or a string of digits)."}],"responses":{"201":{"description":"A cost paid to get goods onto the floor after they arrived (freight, a crane, prep), added to what the goods on one purchase order cost and shared among its arrivals. Rows are only ever added. Its shares are in landed_cost_allocation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LandedCost"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-lenders":{"get":{"operationId":"listFloorPlanLenders","summary":"List floor-plan lenders","tags":["Floor Plans"],"description":"Returns the lenders that finance units on the floor, oldest first, each with its terms (rate, free days, curtailment schedule, payoff days), its Floor Plan Payable account, how many units still owe it and, with cost.view, what is owed. A business that does not floor-plan has none.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of floor-plan lenders and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLenderPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createFloorPlanLender","summary":"Add a floor-plan lender","tags":["Floor Plans"],"description":"Adds a lender that finances units on the floor, with its terms: the yearly interest rate, free days before interest starts, the curtailment schedule (the percent of the amount financed due at each age), and the days after a sale by which a unit must be paid off. Opens the lender's own Floor Plan Payable account (and Floor Plan Interest with the first lender). Needs ledger.post. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFloorPlanLenderInput"}}}},"x-required-permission":"ledger.post","responses":{"201":{"description":"A lender that finances units on the floor (a floor-plan line of credit): who it is and its terms. Units it finances are floor_plan_loan rows. Each lender has its own Floor Plan Payable account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLender"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-lenders/{floor_plan_lender_id}":{"patch":{"operationId":"updateFloorPlanLender","summary":"Change a floor-plan lender's terms","tags":["Floor Plans"],"description":"Changes the fields sent. Terms apply to all of the lender's units from then on, as a floor-plan line reprices. A lender is retired (is_active false) only when none of its units still owe it. Needs ledger.post. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFloorPlanLenderInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"floor_plan_lender_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"ledger.post","responses":{"200":{"description":"A lender that finances units on the floor (a floor-plan line of credit): who it is and its terms. Units it finances are floor_plan_loan rows. Each lender has its own Floor Plan Payable account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLender"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-loans":{"get":{"operationId":"listFloorPlanLoans","summary":"List units on floor plans","tags":["Floor Plans"],"description":"Returns floor-plan loans, oldest first, each with the unit, what is owed, days floored, interest accrued so far, the next curtailment and its amount, and for a sold unit the payoff due date. Filter to what needs paying: due=payoff for units sold and not paid off, due=curtailment for curtailments due by due_by (default today). Needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["open","paid_off"]},"in":"query","name":"status","required":false,"description":"Return only open or only paid-off loans."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"floor_plan_lender_id","required":false,"description":"Return only this lender's loans."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"serialized_unit_id","required":false,"description":"Return only loans on this unit."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only loans on units in stock at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","enum":["payoff","curtailment"]},"in":"query","name":"due","required":false,"description":"payoff: units sold and not yet paid off. curtailment: open loans with a curtailment due on or before due_by."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"due_by","required":false,"description":"With due=curtailment or due=payoff: only what falls due on or before this date. Default: no limit for payoff, today for curtailment."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of floor-plan loans and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLoanPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createFloorPlanLoan","summary":"Put a unit on a floor plan","tags":["Floor Plans"],"description":"Records that a lender financed a serialized unit: the amount financed and the date it paid. When the lender paid the vendor for the unit as it arrived on a purchase order (the usual case), leave ledger_account_id out: the amount comes off Inventory Received Not Billed and onto the lender's Floor Plan Payable, and the vendor's bill for the unit is settled by the lender. Otherwise name the account the money came from. A unit can also be floored as it is received (receivePurchaseOrder). Needs ledger.post, and cost.view to see the loan it answers with. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFloorPlanLoanInput"}}}},"x-required-permission":"ledger.post","responses":{"201":{"description":"One serialized unit financed by a floor-plan lender: what was financed, what has been paid on it, and when it was paid off. What is owed and when is in floor_plan_loan_status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLoan"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-loans/{floor_plan_loan_id}":{"get":{"operationId":"getFloorPlanLoan","summary":"Get a unit's floor-plan loan","tags":["Floor Plans"],"description":"Returns one floor-plan loan with what is owed, interest accrued, what is due next, every payment, and next: what can be done with it now. Needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"floor_plan_loan_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One serialized unit financed by a floor-plan lender: what was financed, what has been paid on it, and when it was paid off. What is owed and when is in floor_plan_loan_status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLoan"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-loans/{floor_plan_loan_id}/payments":{"post":{"operationId":"payFloorPlanLoan","summary":"Record a payment on a unit's floor-plan loan","tags":["Floor Plans"],"description":"Records money paid to the lender on one unit: a curtailment (principal_amount), the payoff (pay_off true pays everything still owed), interest billed on the unit (interest_amount), or several at once, from the account named (the bank, usually). Principal comes off the lender's Floor Plan Payable; interest goes to Floor Plan Interest. Needs ledger.post, and cost.view to see the loan it answers with. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayFloorPlanLoanInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"floor_plan_loan_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"ledger.post","responses":{"200":{"description":"One serialized unit financed by a floor-plan lender: what was financed, what has been paid on it, and when it was paid off. What is owed and when is in floor_plan_loan_status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLoan"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-lenders/{floor_plan_lender_id}/interest":{"post":{"operationId":"payFloorPlanInterest","summary":"Record a lender's interest bill","tags":["Floor Plans"],"description":"Records interest paid to a lender for all its units at once, as most lenders bill it monthly, from the account named. It goes to Floor Plan Interest. Interest billed on one unit can be recorded on its loan instead (payFloorPlanLoan). Needs ledger.post. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayFloorPlanInterestInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"floor_plan_lender_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"ledger.post","responses":{"200":{"description":"A lender that finances units on the floor (a floor-plan line of credit): who it is and its terms. Units it finances are floor_plan_loan rows. Each lender has its own Floor Plan Payable account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanLender"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-interest":{"get":{"operationId":"getFloorPlanInterest","summary":"Get floor-plan interest per lender and month","tags":["Floor Plans"],"description":"Returns, for each lender and month from from to to, the interest accrued on its units (estimated at the lender's rate on each day's balance) and the interest recorded as paid. Compare them to check a lender's bill. Needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":true,"description":"The first day to count."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":true,"description":"The last day to count, at most 400 days after from."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"floor_plan_lender_id","required":false,"description":"Count only this lender."}],"x-required-permission":"cost.view","responses":{"200":{"description":"Floor-plan interest per lender and month: accrued, estimated, and paid, as recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanInterest"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/floor-plan-lenders/{floor_plan_lender_id}/audit":{"get":{"operationId":"getFloorPlanAudit","summary":"Get a lender's floor-plan audit","tags":["Floor Plans"],"description":"Returns every unit the lender financed that is not paid off, at one location or all of them: where the system says it is, whether it is on hand, held for a customer, missing on the last stock count that covered its store, sold and not paid off, or out of stock otherwise, and the last count that saw it. It is the list to walk the floor with when the lender's auditor comes, or to check against the lender's own. Needs cost.view, or stock.adjust at the location asked for, which shows it without the amounts.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only units at this location, or that last left it. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"floor_plan_lender_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A lender's floor-plan audit: every unit it financed that is not paid off, where the system says it is, what has happened to it, and when a stock count last saw it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlanAudit"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/equipment":{"get":{"operationId":"listEquipment","summary":"Find customers' equipment","tags":["Service"],"description":"Returns equipment customers own, oldest first, each with its warranties. Filter by customer or site; search takes a serial number, name, brand or model.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's equipment. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"site_id","required":false,"description":"Return only equipment at this site."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of equipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EquipmentPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createEquipment","summary":"Record a customer's equipment","tags":["Service"],"description":"Records equipment a customer owns that the business did not sell them, so it can be serviced. A unit the business sells becomes the customer's equipment by itself when it is handed over.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEquipmentInput"}}}},"responses":{"201":{"description":"One unit a customer owns that the tenant may service: a hot tub, a swim spa, a sauna. Created automatically when a serialized unit is handed to a customer, and by hand for equipment bought elsewhere. Work orders, warranties and recurring plans point at it. schema.org: IndividualProduct, as owned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Equipment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/equipment/{equipment_id}":{"get":{"operationId":"getEquipment","summary":"Get a piece of equipment","tags":["Service"],"description":"Returns one piece of equipment with its warranties and whether each is in force today. listWorkOrders with equipment_id gives its service history.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"equipment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One unit a customer owns that the tenant may service: a hot tub, a swim spa, a sauna. Created automatically when a serialized unit is handed to a customer, and by hand for equipment bought elsewhere. Work orders, warranties and recurring plans point at it. schema.org: IndividualProduct, as owned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Equipment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateEquipment","summary":"Update a piece of equipment","tags":["Service"],"description":"Changes the fields sent: where it is installed, notes, when it was removed. Its owner is fixed; when it changes hands, retire it (is_active false) and record it for the new owner.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEquipmentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"equipment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One unit a customer owns that the tenant may service: a hot tub, a swim spa, a sauna. Created automatically when a serialized unit is handed to a customer, and by hand for equipment bought elsewhere. Work orders, warranties and recurring plans point at it. schema.org: IndividualProduct, as owned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Equipment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/equipment/{equipment_id}/warranties":{"post":{"operationId":"addEquipmentWarranty","summary":"Add a warranty to equipment","tags":["Service"],"description":"Records one warranty on a piece of equipment: what it covers, who stands behind it, and from when to when. A manufacturer's warranty names the vendor claims go to. Work under a warranty in force is charged at 0, so this needs warranty.manage: at the location of each open warranty job on the equipment, or anywhere when there is none. Returns the equipment.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddEquipmentWarrantyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"equipment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One unit a customer owns that the tenant may service: a hot tub, a swim spa, a sauna. Created automatically when a serialized unit is handed to a customer, and by hand for equipment bought elsewhere. Work orders, warranties and recurring plans point at it. schema.org: IndividualProduct, as owned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Equipment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/equipment/{equipment_id}/warranties/{equipment_warranty_id}":{"patch":{"operationId":"updateEquipmentWarranty","summary":"Update a warranty","tags":["Service"],"description":"Changes the fields sent on one warranty, or voids it with is_active false. Changing what it covers (dates, provider, vendor, is_active) needs warranty.manage, as adding one does; its name and terms do not. Once a claim is made under it, who stands behind it is fixed. Returns the equipment.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEquipmentWarrantyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"equipment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"equipment_warranty_id","required":true,"description":"The warranty, from the equipment's warranties."}],"responses":{"200":{"description":"One unit a customer owns that the tenant may service: a hot tub, a swim spa, a sauna. Created automatically when a serialized unit is handed to a customer, and by hand for equipment bought elsewhere. Work orders, warranties and recurring plans point at it. schema.org: IndividualProduct, as owned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Equipment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders":{"get":{"operationId":"listWorkOrders","summary":"Find work orders","tags":["Service"],"description":"Returns work orders, oldest first. Filter by status, customer, equipment or location; search takes a work order number or words from the problem.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["open","completed","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only work orders in this status."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's work orders. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"equipment_id","required":false,"description":"Return only work on this equipment: its service history."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only work orders this location is responsible for. Accepts the record's id or its branch code."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."}],"responses":{"200":{"description":"One page of work orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrderPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"openWorkOrder","summary":"Open a work order","tags":["Service"],"description":"Records a job to do for a customer: what they reported, on which equipment, at which site. Opened from a kind of job (job_template_id, listJobTemplates), it gets its steps, parts, pricing, checklist and charges. Then book a visit with scheduleAppointment; the technician records time, parts and photos on it. Check the equipment's warranties first (warranties_in_force): set is_warranty when one covers the work, which needs warranty.manage at its location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenWorkOrderInput"}}}},"responses":{"201":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}":{"get":{"operationId":"getWorkOrder","summary":"Get a work order","tags":["Service"],"description":"Returns one job with its visits, charges orders, warranty claims, whether a warranty is in force, and its field work: steps, time, parts used, photos, checks and the customer's sign-offs. With cost.view, what it earned and cost (economics). next reads as the job's to-do list.","parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateWorkOrder","summary":"Update a work order","tags":["Service"],"description":"Changes the fields sent: the problem, what was done so far, whether it is warranty work, which site or equipment. At the job's location, is_warranty, and the equipment of warranty work, need warranty.manage and are fixed once parts or time are recorded under them; a labor item cheaper than the job's own (null being the business's) needs order.discount; dropping a required checklist needs labor.manage. Moving the job to another location_id needs labor.manage at both, and anything else sent with it is weighed at both.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/charges":{"post":{"operationId":"addWorkOrderCharges","summary":"Charge parts and labor on a work order","tags":["Service"],"description":"Adds parts and labor to the job's open charges order, or opens a new one for the customer at the work order's location and site when there is none (the first time, or after the last was handed over and closed). Hand them over and take payment on the sales order. A price below the item's needs order.discount.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddWorkOrderChargesInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/complete":{"post":{"operationId":"completeWorkOrder","summary":"Complete a work order","tags":["Service"],"description":"Closes the job with what was found and done. Final: what was asked for and done is then fixed, and sending it again changes nothing. Its charges are still handed over and paid on the sales order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteWorkOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/cancel":{"post":{"operationId":"cancelWorkOrder","summary":"Cancel a work order","tags":["Service"],"description":"Records that the work will not be done, and cancels its scheduled visits. Final.","parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/warranty-summary":{"get":{"operationId":"getWarrantySummary","summary":"See where warranty money stands with each manufacturer","tags":["Warranty"],"description":"For each manufacturer: claims not yet sent and how many are past its filing deadline, completed warranty work never claimed, claims waiting on an answer, what it owes on approvals and how old, failed parts still to ship back or held, and what claims paid short left the business to absorb. Start here to chase warranty money; listWarrantyClaims and listFailedParts give the records behind each figure.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"vendor_id","required":false,"description":"Return only this manufacturer."}],"responses":{"200":{"description":"Where warranty money stands with each manufacturer: what is unsent, waiting, owed and how old.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarrantySummary"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/vendors/{vendor_id}/warranty-terms":{"get":{"operationId":"getWarrantyTerms","summary":"Get how a manufacturer takes warranty claims","tags":["Warranty"],"description":"Returns one manufacturer's claim terms as the business set them up: how and by when to file, what a claim must carry before it is sent, what happens to failed parts, and what it pays. A manufacturer with none set returns the defaults (is_set false): nothing required, failed parts discarded, nothing expected.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"vendor_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"How one manufacturer takes warranty claims, as the dealer has set it up: how and by when a claim is filed, what it must carry, what happens to the failed parts, and what the manufacturer pays. At most one per vendor. A vendor with none takes claims on the defaults: nothing required, failed parts discarded, no expected reimbursement worked out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarrantyTerms"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateWarrantyTerms","summary":"Set how a manufacturer takes warranty claims","tags":["Warranty"],"description":"Changes the fields sent on one manufacturer's claim terms, starting from the defaults if none are set. They decide what a claim to it must carry before it is sent, which failed parts are held or shipped back, what it is expected to pay, and where a short payment is absorbed. A claim already sent keeps the expected amount worked out when it went. Needs warranty.manage. Requires the warranty.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWarrantyTermsInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"vendor_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"warranty.manage","responses":{"200":{"description":"How one manufacturer takes warranty claims, as the dealer has set it up: how and by when a claim is filed, what it must carry, what happens to the failed parts, and what the manufacturer pays. At most one per vendor. A vendor with none takes claims on the defaults: nothing required, failed parts discarded, no expected reimbursement worked out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarrantyTerms"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/warranty-claims":{"get":{"operationId":"listWarrantyClaims","summary":"List warranty claims","tags":["Warranty"],"description":"Returns claims on manufacturers, oldest first, each with what its manufacturer expects to pay, what it still lacks to be sent, its filing deadline and its failed parts. Filter by status to see what is waiting on a manufacturer (submitted) or owed and not yet paid (approved); past_filing_deadline finds claims not sent in time, and parts_outstanding those with failed parts still to ship or held.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["draft","submitted","approved","denied","paid","withdrawn"]},"in":"query","name":"status","required":false,"description":"Return only claims in this status."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"vendor_id","required":false,"description":"Return only claims on this manufacturer."},{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"query","name":"work_order_id","required":false,"description":"Return only claims for this work order. Accepts the record's id or its work order number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"past_filing_deadline","required":false,"description":"true: only claims not sent by their manufacturer's deadline; false: only the rest."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"parts_outstanding","required":false,"description":"true: only claims with failed parts on hand the manufacturer wants back or kept."}],"responses":{"200":{"description":"One page of warranty claims.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarrantyClaimPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createWarrantyClaim","summary":"Start a warranty claim","tags":["Warranty"],"description":"Starts a claim on a manufacturer for warranty work on a work order, as a draft, with what it carries and the failed parts the repair took out. The work order must be warranty work; naming the warranty sets the vendor, and with neither named the job's one manufacturer's warranty in force on the days the warranty work was done is taken (the days its parts and time were recorded under warranty, else the day it was completed). A warranty named must cover those days too. Left out, failed_parts are the parts recorded as used under warranty on the job with what the technician said was taken out, and photos_attached is true when a photo on the job shows the fault. The claim's missing field says what that manufacturer still requires; then submit it with updateWarrantyClaim.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWarrantyClaimInput"}}}},"responses":{"201":{"description":"A claim on a manufacturer to pay for warranty work the tenant did: the parts and labor on one work order. It tracks the claim from draft to paid or denied, so warranty work the tenant is owed for is never forgotten.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarrantyClaim"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/warranty-claims/{warranty_claim_id}":{"patch":{"operationId":"updateWarrantyClaim","summary":"Move a warranty claim along","tags":["Warranty"],"description":"Changes the fields sent. Status moves draft, submitted (once it carries what the manufacturer requires), then approved (with amount_approved, then owed to the business) or denied; approved moves to paid, with amount_paid when the manufacturer paid less (the rest is absorbed where its terms say), or to denied when it withdraws the approval, which reverses what was posted. Once answered, the amounts are fixed: to correct an approved amount, deny the claim and start a new one. A draft that will not be sent moves to withdrawn. Dates are recorded by the system. Recording the answer (approved, denied), the payment (paid, amount_paid) or amount_approved needs warranty.manage at the work order's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWarrantyClaimInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"warranty_claim_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A claim on a manufacturer to pay for warranty work the tenant did: the parts and labor on one work order. It tracks the claim from draft to paid or denied, so warranty work the tenant is owed for is never forgotten.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarrantyClaim"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/failed-parts":{"get":{"operationId":"listFailedParts","summary":"List failed parts from warranty repairs","tags":["Warranty"],"description":"Returns failed parts tracked on warranty claims, oldest first. outstanding=true gives the parts still on hand that a manufacturer wants back or kept, on claims it may still pay: the ones whose credit is at risk. Each says what to do with it next and, when held, until when.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["on_hand","shipped","discarded"]},"in":"query","name":"status","required":false,"description":"Return only parts in this status."},{"schema":{"type":"string","enum":["discard","hold","return"]},"in":"query","name":"disposition","required":false,"description":"Return only parts with this disposition."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"vendor_id","required":false,"description":"Return only parts on claims to this manufacturer."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"warranty_claim_id","required":false,"description":"Return only parts on this claim."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"outstanding","required":false,"description":"true: only parts on hand to return or held, on claims not withdrawn or denied; false: only the rest."}],"responses":{"200":{"description":"One page of failed parts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailedPartPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/warranty-claims/{warranty_claim_id}/failed-parts":{"post":{"operationId":"addFailedPart","summary":"Track a failed part on a warranty claim","tags":["Warranty"],"description":"Records one part the warranty repair took out, on hand, with what is to happen to it from the manufacturer's terms (discard, hold or return) unless disposition is sent. Naming the catalog item fills in its part number and description. A disposition other than the terms', or hold_days, needs warranty.manage at the work order's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddFailedPartInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"warranty_claim_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"One failed part a warranty repair took out, tracked until the manufacturer has it back or it may be thrown away, so no credit is lost for a part left on a shelf.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailedPart"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/warranty-claims/{warranty_claim_id}/failed-parts/{failed_part_id}":{"patch":{"operationId":"updateFailedPart","summary":"Ship back, discard or correct a failed part","tags":["Warranty"],"description":"Changes the fields sent. status shipped records it going back to the manufacturer (shipped_on defaults to today; the return authorization number is required when its terms say so). status discarded records it thrown away, which a part to return or still in its hold period cannot be: change disposition first if the manufacturer said to scrap it, in a request of its own (needs warranty.manage, as does hold_days). Shipped and discarded are final.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFailedPartInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"warranty_claim_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"failed_part_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One failed part a warranty repair took out, tracked until the manufacturer has it back or it may be thrown away, so no credit is lost for a part left on a shelf.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailedPart"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments":{"get":{"operationId":"listAppointments","summary":"See the schedule","tags":["Field Work"],"description":"Returns appointments in start order within a window, with crew, site, what each delivery hands over and whether its site is ready. A day's schedule for a location or a person is starts_from and starts_before a day apart with location_id or staff_member_id. Deliveries whose sites are not shown ready in time are readiness_overdue=true.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"starts_from","required":false,"description":"Return only appointments starting at or after this time."},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"starts_before","required":false,"description":"Return only appointments starting before this time."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only this location's appointments. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only appointments this person is going on."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's appointments. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["delivery","service","consultation"]},"in":"query","name":"kind","required":false,"description":"Return only appointments of this kind."},{"schema":{"type":"string","enum":["scheduled","completed","cancelled","failed"]},"in":"query","name":"status","required":false,"description":"Return only appointments in this status."},{"schema":{"type":"string","enum":["ready","not_ready","unchecked"]},"in":"query","name":"readiness","required":false,"description":"Return only deliveries whose site readiness is this."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"readiness_overdue","required":false,"description":"Send true for only scheduled deliveries not ready by their ready_by date."}],"responses":{"200":{"description":"One page of appointments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"scheduleAppointment","summary":"Book a delivery, service call or consultation","tags":["Field Work"],"description":"Books a visit with a customer in a window, with its crew and, for a delivery, the order lines it hands over (each for this customer and site, on an open order). Overlapping bookings for one person are allowed and shown in also_booked. A delivery booked from a location other than where its orders were sold moves their goods between stores, so it needs stock.transfer at the location it is booked from. A care plan's visit names the plan's order for it (subscription_order_id): completing the visit hands that order's services over, and its time is covered by the plan.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleAppointmentInput"}}}},"responses":{"201":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}":{"get":{"operationId":"getAppointment","summary":"Get an appointment","tags":["Field Work"],"description":"Returns one appointment with its crew, the site and how to get in, what a delivery hands over, whether its site is ready and what is in the way, its outside parties, photos and sign-off, and next.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateAppointment","summary":"Move or change an appointment","tags":["Field Work"],"description":"Changes the fields sent: a new window, the site, the instructions. A delivery goes to the site its orders go to, so it can follow an order whose site changed but not leave one behind. Moving a visit sends its confirmed outside parties back to requested, to confirm the new time, and withdraws its messages still queued (they give the old time); once the crew has set off it cannot move. Once completed, failed or cancelled only the outcome can change.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAppointmentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/crew":{"post":{"operationId":"setAppointmentCrew","summary":"Say who is going","tags":["Field Work"],"description":"Replaces the crew on an appointment. Mark one is_lead: a delivery is recorded as handed over by its lead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAppointmentCrewInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/lines":{"post":{"operationId":"setDeliveryLines","summary":"Say what a delivery hands over","tags":["Field Work"],"description":"Replaces the order lines a scheduled delivery hands over, and how many of each. They must be for the appointment's customer and site. A line left out is taken off, even one whose order now goes elsewhere.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetDeliveryLinesInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/en-route":{"post":{"operationId":"markAppointmentEnRoute","summary":"Record that the crew has set off","tags":["Field Work"],"description":"Records that the crew is on its way now, with when they expect to arrive (eta), which tells the customer (the appointment.en_route event). Where the business requires a ready site before a crew is sent (dispatch_requires_ready_site), a delivery not shown ready is refused with what is in the way, unless the key holds delivery.override (where the visit is booked and where its orders were sold) and sends override_reason, which is recorded with who gave it. The same rule holds if the crew records arriving or the delivery done without setting off first. Sending it again changes nothing. Only a key acting for someone on the visit's crew, or holding schedule.manage where it is booked, records it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkAppointmentEnRouteInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/arrival":{"post":{"operationId":"recordAppointmentArrival","summary":"Record that the crew has arrived","tags":["Field Work"],"description":"Records when the crew arrived (now, unless arrived_at is sent), what they found, and photos of the unit before unloading and the site as found. Each photo is uploaded with uploadFile first. Arrival is recorded once; sending it again adds only photos not already attached. Only a key acting for someone on the visit's crew, or holding schedule.manage where it is booked, records it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordAppointmentArrivalInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/photos":{"post":{"operationId":"addAppointmentPhotos","summary":"Attach photos or documents to a visit","tags":["Field Work"],"description":"Attaches uploaded files to a visit as photos (JPEG, PNG or WebP) or documents (PDF), at any time, even after it is over. A file the visit already has is not attached twice. A visit takes at most 60.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAppointmentPhotosInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/complete":{"post":{"operationId":"completeAppointment","summary":"Record that an appointment happened","tags":["Field Work"],"description":"Marks the visit done, with what happened. A delivery is recorded with completeDelivery, with the customer's sign-off; this completes one without a sign-off only where the business does not require one. Completing a delivery hands its lines over: stock leaves and the sale counts, credited to the lead. Only what is still to hand over is handed over, and each line's quantity_handed_over says how many this visit did. If any line cannot be handed over the completion fails with the reason and nothing changes. Final; sending it again changes nothing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinishAppointmentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/cancel":{"post":{"operationId":"cancelAppointment","summary":"Cancel an appointment","tags":["Field Work"],"description":"Records that the visit will not happen, cancels its outside parties not yet confirmed, and withdraws its messages still queued. Final: book a new appointment to try again. A delivery the crew reached is not cancelled: record it with failDelivery.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinishAppointmentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/job-templates":{"get":{"operationId":"listJobTemplates","summary":"List the kinds of job the business does","tags":["Field Service"],"description":"Returns the kinds of job (job templates), oldest first, each with its steps and the items it charges, includes and suggests. Open a work order from one with openWorkOrder's job_template_id, which also takes the template's code.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of kinds of job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobTemplatePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createJobTemplate","summary":"Add a kind of job","tags":["Field Service"],"description":"Adds a kind of job the business does again and again: its steps with standard times, how its labor is priced (hourly, or flat with its charges), the parts its flat price includes, parts to bring, and the service checklist the technician answers. Labor is charged as a service item sold by the hour. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobTemplateInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A kind of job the business does again and again, such as a spa winterize or a heater replacement: its steps with their standard times, the parts it uses, how its labor is priced, and the checklist the technician answers. Opening a work order from it copies all of this onto the job, so a later change to the template never changes a job already open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobTemplate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/job-templates/{job_template_id}":{"get":{"operationId":"getJobTemplate","summary":"Get a kind of job","tags":["Field Service"],"description":"Returns one kind of job, by id or code, with its steps and lines in use.","parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"job_template_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its job template code."}],"responses":{"200":{"description":"A kind of job the business does again and again, such as a spa winterize or a heater replacement: its steps with their standard times, the parts it uses, how its labor is priced, and the checklist the technician answers. Opening a work order from it copies all of this onto the job, so a later change to the template never changes a job already open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobTemplate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateJobTemplate","summary":"Change a kind of job","tags":["Field Service"],"description":"Changes the fields sent. tasks and lines, when sent, replace the steps and lines in use. Jobs already opened keep what was copied onto them. Retire it with is_active false. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateJobTemplateInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"job_template_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its job template code."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A kind of job the business does again and again, such as a spa winterize or a heater replacement: its steps with their standard times, the parts it uses, how its labor is priced, and the checklist the technician answers. Opening a work order from it copies all of this onto the job, so a later change to the template never changes a job already open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobTemplate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/technician-day":{"get":{"operationId":"getTechnicianDay","summary":"Get a technician's day: stops, jobs and parts to bring","tags":["Field Service"],"description":"Returns one technician's visits on a day in the order they go (what is over first, then as put in order with putRouteInOrder), each with the customer, address, equipment and the warranties in force on it, the job's problem, steps and checklist; their running timer; and the parts the day's jobs need against what their van holds. next starts with loading what is short (transferStock, offered only to a key holding stock.transfer at the van's home), then the first stop.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":true,"description":"The technician: the caller's own staff member (getIdentity), or another."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date","required":false,"description":"The day. Defaults to today at their home location."}],"responses":{"200":{"description":"One technician's day: where to go, what to do and what to bring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TechnicianDay"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/tasks":{"post":{"operationId":"addWorkOrderTask","summary":"Add a step to a job","tags":["Field Service"],"description":"Adds a step found on site, such as a second repair, with who pays for time and parts on it. Billing defaults from the job: warranty work to warranty, a flat-priced job to included, else customer. Steps change only while the job is open. Returns the work order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddWorkOrderTaskInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/tasks/{work_order_task_id}":{"patch":{"operationId":"updateWorkOrderTask","summary":"Tick off or change a step of a job","tags":["Field Service"],"description":"Changes the fields sent on one step: done (with done_at from the device) or skipped, its notes, who pays. Who pays is fixed once time on it is charged; no_charge needs order.discount. Returns the work order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkOrderTaskInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"work_order_task_id","required":true,"description":"The step, from getWorkOrder's tasks."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/labor-entries":{"get":{"operationId":"listLaborEntries","summary":"List time recorded: the timesheet","tags":["Field Service"],"description":"Returns time recorded on jobs, oldest first. Filter by person, job, days worked, running timers or time not yet charged.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Only this person's time."},{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"query","name":"work_order_id","required":false,"description":"Only time on this job. Accepts the record's id or its work order number (as a number or a string of digits)."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"Only time worked on or after this day."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"Only time worked on or before this day."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"running","required":false,"description":"Only timers still running."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"unbilled","required":false,"description":"Only time not yet charged or settled, voided left out."}],"responses":{"200":{"description":"One page of time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaborEntryPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/labor":{"post":{"operationId":"recordLaborTime","summary":"Start a timer or record time on a job","tags":["Field Service"],"description":"Records time one person spent on a job: started_at alone starts a timer (stopping their running one there), started_at with ended_at is time already done, minutes alone is a duration. Times are the device's: at most 5 minutes ahead, and no older than the business takes without labor.manage. Who pays defaults from the step, else the job; travel to the business's travel policy. Overlapping time for one person is refused. Time is charged when the visit or job is completed. A repeat of the same idempotency_key returns the entry; a changed request under it is refused.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordLaborTimeInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"201":{"description":"Time one technician spent on one job: travelling to it or working on it, timed with a start and a stop or entered as a duration. It is what the customer is charged for by the hour, what warranty labor is claimed from, and what the job cost in labor. Payroll is not kept here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaborEntry"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/labor-entries/{labor_entry_id}":{"patch":{"operationId":"updateLaborEntry","summary":"Stop a timer, or correct or void time","tags":["Field Service"],"description":"Stops a running timer (stop, or ended_at from the device), corrects an entry not yet charged, or voids one recorded in error (void_reason). Another person's time needs labor.manage at the job's location. Time already charged is fixed: take back its labor line on the order instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLaborEntryInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"labor_entry_id","required":true,"description":"The time, from listLaborEntries or getWorkOrder."}],"responses":{"200":{"description":"Time one technician spent on one job: travelling to it or working on it, timed with a start and a stop or entered as a duration. It is what the customer is charged for by the hour, what warranty labor is claimed from, and what the job cost in labor. Payroll is not kept here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaborEntry"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/parts":{"post":{"operationId":"recordPartsUsed","summary":"Record parts used on a job","tags":["Field Service"],"description":"Records parts a technician used. Each is handed over at once: stock leaves their van (or the job's store for work there), and it goes on the job's charges order at the item's price for the customer, or at 0 under warranty, included in a flat price, or no charge (order.discount). A price below the item's needs the discount allowance, weighed at the charges order's location. Warranty parts carry the failed part's number for the claim. A part already recorded with the same idempotency_key is not used again; a changed part under it is refused. Returns the work order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPartsUsedInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/part-uses/{service_part_use_id}/take-back":{"post":{"operationId":"takeBackPart","summary":"Take back a part recorded in error","tags":["Field Service"],"description":"Undoes a part used on a job, all of it or some: the stock goes back where it came from at its cost, and the charge comes off its order, with no restocking fee. Once per part. Whoever recorded the part takes it back while the job is open; anyone else, or after the job is closed, needs order.return at the location it goes back to. A changed request under an idempotency_key already used is refused. Returns the work order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TakeBackPartInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"service_part_use_id","required":true,"description":"The part used, from getWorkOrder's parts_used."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/photos":{"post":{"operationId":"addWorkOrderPhotos","summary":"Attach photos to a job","tags":["Field Service"],"description":"Attaches uploaded photos to a job, each saying what it shows: the fault as found, the repair as left, the serial plate. Taken after the job is completed they are still accepted (a device uploading late). A file the job already has is not attached twice. A job takes at most 100. Returns the work order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddWorkOrderPhotosInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/checklist":{"post":{"operationId":"recordServiceChecklist","summary":"Record a job's service checklist","tags":["Field Service"],"description":"Records the technician's answers to the job's service checklist (or another service checklist), with photos per question. The outcome is worked out from the answers unless sent; passed is refused if any answer fails. A job that requires its checklist is not completed until one is recorded with every required question answered; a failed check completes it. A repeat of the same idempotency_key records nothing more; a changed request under it is refused. Returns the work order; getSiteCheck shows the check in full.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordServiceChecklistInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/sign-offs":{"post":{"operationId":"signWorkOrder","summary":"Record the customer's signature on a job","tags":["Field Service"],"description":"Records the customer signing for a job under the business's statement: authorizing the work before it is done (up to amount_authorized, or the charges shown), or accepting it as done. Upload the signature (a PNG) with uploadFile first. A completion with nobody there is customer_absent and needs a photo on the job, and labor.manage where the business requires a signature. A changed request under an idempotency_key already used is refused. Returns the work order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignWorkOrderInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/finish-service":{"post":{"operationId":"completeServiceVisit","summary":"Finish a service visit: charge its time and services","tags":["Field Service"],"description":"The technician's one done call. Stops the crew's timers on the visit at finished_at (now when left out), records the diagnosis and resolution, completes the visit, which charges its time (customer time rounded by the business's rules, warranty time at 0) and hands over the job's services such as a service-call fee, with complete_work_order completes the job too, and then records the customer's sign-off, so it shows the charges they sign for. Sending it again for a finished visit changes nothing. Returns what the job's charges now come to.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteServiceVisitInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A service visit as finished, and what its job's charges came to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceVisitResult"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/finish-service/price":{"post":{"operationId":"priceServiceVisit","summary":"What finishing a service visit would charge","tags":["Field Service"],"description":"Writes nothing. Says what completeServiceVisit would charge if the visit were finished at finished_at (now when left out): each order of the job line by line (parts, time in hours at the business's rate, service-call fees), who pays for each line, the tax, the total and what is left to pay. Show it to the customer before they sign, then finish with the same finished_at and it charges exactly this. Time on the job but on no visit is charged when the job is completed, and is not in it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceServiceVisitInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"What finishing a service visit would charge, priced by the same rules and then not kept: what the customer is shown before they sign.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceVisitCharges"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/work-orders/{work_order_id}/bill-labor":{"post":{"operationId":"billWorkOrderLabor","summary":"Charge time recorded after a job was completed","tags":["Field Service"],"description":"Charges time a device uploaded after the job was completed: gathered into labor lines on the job's open charges order, or a new one, and handed over. The visit's billing minimum and rounding are worked out on all its time, so late time pays only what the visit's total comes to beyond what it was charged. Time already charged is not charged again; with nothing to charge it changes nothing. Returns the work order, whose labor_entries show each entry's line.","parameters":[{"schema":{"type":"string","x-natural-key":"work_order_number"},"in":"path","name":"work_order_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its work order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One job to do for a customer: a repair, a service, an installation. It says what was asked for and what was done. The visits to do it are appointments, and its parts and labor are lines on the sales orders that name it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/staff/{staff_member_id}/labor-cost-rates":{"get":{"operationId":"getLaborCostRates","summary":"List what a technician's hour costs","tags":["Staff"],"description":"Returns a technician's cost rates, newest first: what an hour of their time costs the business from each date. Reported only, never posted. Needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"A technician's cost rates over time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaborCostRates"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"setLaborCostRate","summary":"Set what a technician's hour costs from a date","tags":["Staff"],"description":"Sets a technician's hourly cost (wages plus burden) from a date, replacing the rate set for that same date. Job margins are restated with it, since labor cost is reported and never posted. Needs cost.view and staff.manage. Requires the cost.view permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetLaborCostRateInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"staff_member_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"A technician's cost rates over time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaborCostRates"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/service-margin":{"get":{"operationId":"getServiceMargin","summary":"Get what service jobs earned and cost","tags":["Reports"],"description":"Returns completed jobs' revenue and parts cost as posted, labor at each technician's cost rate (reported, not posted) and warranty recovered, with the margin, by job, technician (whoever recorded the most time on it) or kind of job. Rows say when some time had no cost rate. Needs cost.view, and counts only jobs at locations where the key holds sales.view. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":true,"description":"First day, by the day each job was completed at its location."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":true,"description":"Last day."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only jobs this location is responsible for. Accepts the record's id or its branch code."},{"schema":{"default":"work_order","type":"string","enum":["work_order","technician","template"]},"in":"query","name":"group_by","required":false,"description":"What each row is."}],"x-required-permission":"cost.view","responses":{"200":{"description":"What completed jobs earned and cost: revenue and parts cost as posted, labor at cost rates (not posted), and warranty recovered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceMargin"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/day-routes":{"get":{"operationId":"getDayRoutes","summary":"See a store's day as routes: each person's stops in order","tags":["Field Work"],"description":"Returns, for one store and day, the day of each person going on a visit booked from it: every visit they go on that day in the order they go, with the customer, the address and postal code, the window the customer was given and who else goes; and the visits booked there with nobody on them. A stop never put in order says in_order false and comes after those that were, by its window. Put a day in order with putRouteInOrder; give a stop to someone else or move it with moveRouteStop.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":true,"description":"The store, by id or branch code. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date","required":false,"description":"The day. Defaults to today at the store."}],"responses":{"200":{"description":"A store's day of visits as routes: each person's stops in the order they go.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DayRoutes"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/day-routes/order":{"post":{"operationId":"putRouteInOrder","summary":"Put a person's stops for a day in the order they go","tags":["Field Work"],"description":"Says the order a person goes to their visits still to come on a day, first stop first: every one, each once (read them with getDayRoutes). Visits they share with others (a delivery crew) take the same order on those others' days, each keeping their own visits where they were. A window sent with a stop narrows or moves the one the customer was given, on the same day; the customer is told by sendMessage, or by the business's automation for appointment.rescheduled. A window cannot move once the crew has set off. Sending the same order again changes nothing. Needs schedule.manage where each visit is booked. Returns the person's day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutRouteInOrderInput"}}}},"responses":{"200":{"description":"One person's visits on one day, in the order they go.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Route"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/move":{"post":{"operationId":"moveRouteStop","summary":"Move a stop to another person, day or place on the day","tags":["Field Work"],"description":"Moves a visit still to come when a customer calls or someone is out. to_staff_member_id goes instead of from_staff_member_id (needed only when several are on it), as lead if they were; the rest of a crew stays. date moves it to that day at its store, its window keeping its times of day unless a new window is sent. position is where it comes on the day of whoever takes it (or of its lead); left out, a stop that changes day or person goes last. Moving the window withdraws its messages still queued and confirmed outside parties go back to requested, as with updateAppointment. Refused once the crew has set off. Sending it again changes nothing, including a hand-over already made. Needs schedule.manage where the visit is booked. Returns the day it is now on.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveRouteStopInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One person's visits on one day, in the order they go.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Route"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/files":{"post":{"operationId":"uploadFile","summary":"Upload a photo, signature or document","tags":["Files"],"description":"Stores a file (JPEG, PNG or WebP image, or PDF, at most 5 MiB) sent as base64, and returns its id to attach it: to a visit (addAppointmentPhotos, recordAppointmentArrival), a site check (recordSiteCheck) or a sign-off (completeDelivery, failDelivery). The type must match the bytes. The same bytes uploaded again are the same file (already_stored), so a retry is harmless.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadFileInput"}}}},"responses":{"201":{"description":"A file stored, ready to attach by its id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadedFile"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/files/{file_id}":{"get":{"operationId":"getFile","summary":"Get a file","tags":["Files"],"description":"Returns a file's type, size, SHA-256 and where it is attached, with its bytes as base64 unless include_content=false.","parameters":[{"schema":{"default":"true","type":"string","enum":["true","false"]},"in":"query","name":"include_content","required":false,"description":"Send false for the file's details without its bytes."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"file_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"The bytes of one file someone uploaded: a photo, a signature, a document. Stored once per business however often it is attached, keyed by its SHA-256, and never changed. schema.org: MediaObject.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/attachments/{attachment_id}":{"patch":{"operationId":"updateAttachment","summary":"Recaption or withdraw a photo","tags":["Files"],"description":"Changes a photo or document's caption, or withdraws one taken in error (is_active false) while its visit is still scheduled. A signature and a site check's photos are never withdrawn.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAttachmentInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"attachment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One file attached to a record as a photo, a signature or a document: the unit as it arrived, the pad from a site check, the customer's signature at a delivery. The file's bytes are never changed. An attachment that is part of a finished record cannot be withdrawn.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Attachment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/site-checks":{"get":{"operationId":"listSiteChecks","summary":"List site checks","tags":["Deliveries"],"description":"Returns site checks and the crew's delivery checks, newest first, each with its answers, what failed, the outside parties it calls for and the deliveries it decides for. Filter by customer, site, order, visit, purpose or outcome.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's checks. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"site_id","required":false,"description":"Return only checks of this site."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only checks for this order's delivery. Accepts the record's id or its order number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["passed","failed"]},"in":"query","name":"outcome","required":false,"description":"Return only checks with this outcome."},{"schema":{"type":"string","enum":["site_readiness","delivery"]},"in":"query","name":"purpose","required":false,"description":"site_readiness for site checks only, delivery for the crew's checks at deliveries only."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"appointment_id","required":false,"description":"Return only checks made on this visit: a delivery's crew checks, or a site visit's check."}],"responses":{"200":{"description":"One page of checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteCheckPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"recordSiteCheck","summary":"Record a site check for an order's delivery","tags":["Deliveries"],"description":"Records whether a customer's site is ready for an order's delivery, on site or remotely from the customer's photos: the answers to the business's site readiness checklist (or the one named), with photos uploaded with uploadFile first. The outcome is worked out from the answers unless sent; passed is refused while anything fails. A check is never changed: record another, and the higher check number decides. Each scheduled delivery of the order to the site takes its readiness from it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordSiteCheckInput"}}}},"responses":{"201":{"description":"One time someone answered a checklist: a site check of a customer's site for an order's delivery (on site, or remotely from the customer's photos), or the crew's checks at a delivery. Its answers and photos are added with it and never after.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteCheck"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/site-checks/{checklist_response_id}":{"get":{"operationId":"getSiteCheck","summary":"Get a site check","tags":["Deliveries"],"description":"Returns one check with each question and its answer, what failed, its photos, the outside parties it calls for and the deliveries it decides for, with next.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"checklist_response_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One time someone answered a checklist: a site check of a customer's site for an order's delivery (on site, or remotely from the customer's photos), or the crew's checks at a delivery. Its answers and photos are added with it and never after.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteCheck"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/parties":{"post":{"operationId":"bookOutsideParty","summary":"Book an outside party for a visit","tags":["Deliveries"],"description":"Records someone outside the business the visit depends on: a crane the business books (name its vendor_id), or the customer's own electrician. A delivery is not ready until each is confirmed with arrive_at: a crane inside the window, the customer's electrician finished before it. cost_amount, which may be passed on to the customer if the delivery fails, needs purchase.manage. Returns the visit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookOutsidePartyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointment-parties":{"get":{"operationId":"listOutsideParties","summary":"List outside parties booked for visits","tags":["Deliveries"],"description":"Returns outside parties booked for visits, in the order of the visits, with the customer, site and window: a crane company's bookings for the week, or every party still to confirm.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"vendor_id","required":false,"description":"Return only this vendor's bookings."},{"schema":{"type":"string","enum":["crane","electrician","contractor","inspector","other"]},"in":"query","name":"role","required":false,"description":"Return only parties in this role."},{"schema":{"type":"string","enum":["needed","requested","confirmed","done","no_show","cancelled"]},"in":"query","name":"party_status","required":false,"description":"Return only parties in this status."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only parties of this location's visits. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"starts_from","required":false,"description":"Return only parties of visits starting at or after this time."},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"starts_before","required":false,"description":"Return only parties of visits starting before this time."}],"responses":{"200":{"description":"One page of outside parties, by when they are due.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutsidePartyPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointment-parties/{appointment_party_id}":{"patch":{"operationId":"updateOutsideParty","summary":"Change an outside party's booking","tags":["Deliveries"],"description":"Changes the fields sent: confirm a party with arrive_at, record their reference or what they bill (cost_amount, which needs purchase.manage), or mark them done, no_show or cancelled (final). Returns the visit, with its readiness.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOutsidePartyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_party_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/deliver":{"post":{"operationId":"completeDelivery","summary":"Record a delivery made, with the customer's sign-off","tags":["Deliveries"],"description":"Records that a delivery was made and the customer signed for it, under the business's acceptance statement: upload the signature (a PNG) and any photos with uploadFile first. This completes the delivery and hands its goods over; goods handed over from a store other than where they were sold need stock.transfer there. Note damage or a shortage with accepted_with_exceptions. Goods left with nobody there to sign are customer_absent, with photos, and need delivery.override. Where the business has a delivery checklist, send the crew's answers. Sending the same request again (the same idempotency_key, or the same content) returns the delivery; a different sign-off for one already signed off is refused as a conflict, since a sign-off never changes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteDeliveryInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/fail":{"post":{"operationId":"failDelivery","summary":"Record a delivery refused or not possible","tags":["Deliveries"],"description":"Records why a delivery could not be made or the customer refused it, with notes and photos (uploaded with uploadFile first), and marks it failed. Where the business charges for a wasted trip (redelivery_fee_item_id, redelivery_fee_reasons), the fee is added to the delivery's open order and charged at once: taken from the customer's deposit, or owed; so is what each outside party the business booked bills, where it passes those on (redelivery_party_item_id), for parties marked done first with updateOutsideParty. It needs the crew's arrival and a photo; waive_fee needs order.discount where the delivery's open orders were sold. Sending the same request again (the same idempotency_key, or the same content) returns the delivery as it ended, charging nothing more; a different failure for one already recorded is refused as a conflict.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailDeliveryInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/appointments/{appointment_id}/redeliver":{"post":{"operationId":"scheduleRedelivery","summary":"Book a failed delivery again","tags":["Deliveries"],"description":"Books a new delivery for a failed one: the same customer, site and location, its crew unless another is sent, the goods it did not hand over, and its outside parties as needed again, to confirm for the new window (one whose work was done before the failed day is not booked again). Goods scheduled deliveries already carry are left off; with nothing left to carry it is refused. The instructions say why the last one failed. One redelivery is booked at a time. After a failure because the site was not ready or could not be reached, its site needs a fresh site check before it counts as ready.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleRedeliveryInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"appointment_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"A scheduled visit with a customer: a delivery, a service call, or a consultation such as an in-home site check or a test soak. Completing a delivery hands over the order lines listed on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/delivery-evidence":{"get":{"operationId":"getDeliveryEvidence","summary":"Get the evidence that an order was delivered","tags":["Deliveries"],"description":"Returns everything recorded about getting one order to the customer, for a dispute or a chargeback: the order and its payments; each delivery attempt with when the crew set off, arrived and the customer signed, under what statement, the signature's SHA-256, where it was signed and how far from the site (or why that is unknown), who recorded it and whose permission allowed a waiver, an absent customer or a crew sent to a site not shown ready, exceptions, photos with their hashes, the outside parties, the crew's checklist answers, what was handed over with serial numbers and, for a failed attempt, what it charged; the site checks; and what the customer was told. Read any file with getFile.","parameters":[{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":true,"description":"The order. Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"Everything recorded about getting one order to the customer, for a dispute or a chargeback: the order and its payments, each delivery attempt with its times, sign-off, signature hash, photos and what it handed over, the site checks, and what the customer was told.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryEvidence"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/deliveries-to-book":{"get":{"operationId":"listDeliveriesToBook","summary":"See what is sold and still needs a delivery booked","tags":["Deliveries"],"description":"Returns open orders going to a customer's site with goods or services still to hand over beyond what scheduled deliveries carry, oldest sale first: the lines still to book, the latest site check, and any failed delivery not booked again. Orders charging a work order or a care plan cycle are left out. Book each with scheduleAppointment (or scheduleRedelivery for a failed one). Orders at a location are all shown to a key holding sales.view or schedule.manage there; elsewhere a key sees only those it is credited on.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only orders sold at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's orders. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","x-natural-key":"order_number"},"in":"query","name":"sales_order_id","required":false,"description":"Return only this order, if it is waiting. Accepts the record's id or its order number (as a number or a string of digits)."}],"responses":{"200":{"description":"One page of orders waiting for a delivery to be booked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryToBookPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/orders-awaiting-goods":{"get":{"operationId":"listOrdersAwaitingGoods","summary":"See which customers' orders wait on goods from vendors, and which are late","tags":["Deliveries"],"description":"Returns open orders with a line still to hand over that waits on goods to come: bought for it on a special order, or stock on order for its store that it is next in line for, with each vendor's date and whether it has passed, and anything not ordered at all. Late orders first, then the oldest sale. Record a vendor's new date with updatePurchaseOrderLine and tell the customer with sendMessage. Orders at a location are all shown to a key holding sales.view or purchase.manage there; elsewhere a key sees only those it is credited on.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only orders sold at this store. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's orders. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"late","required":false,"description":"true: only orders whose goods are past their vendor's date. false: only those that are not."}],"responses":{"200":{"description":"One page of orders waiting on goods from vendors.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderAwaitingGoodsPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/time-off":{"get":{"operationId":"listTimeOff","summary":"See who is out","tags":["Field Work"],"description":"Returns time off overlapping the days asked, by first day, each with the scheduled visits the person is on during it. A day's or a week's board asks from and to; a person's asks staff_member_id. Withdrawn time off is left out unless is_active=false is asked.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"Return only time off ending on or after this day."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"Return only time off starting on or before this day."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only this person's time off."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only time off of people who work from this location. Accepts the record's id or its branch code."},{"schema":{"default":"true","type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"true (the default) for time off in force, false for withdrawn."}],"responses":{"200":{"description":"One page of time off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeOffPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"recordTimeOff","summary":"Mark someone out","tags":["Field Work"],"description":"Records days a person is out: sick, on leave, at training. Visits stay booked: the answer lists the scheduled ones they are on in those days, each to give to someone else (setAppointmentCrew) or move (updateAppointment), then tell the customer. Their own time off anyone may record; someone else's needs schedule.manage where they work from (everywhere for someone with no home location).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordTimeOffInput"}}}},"responses":{"201":{"description":"Days a staff member is out: sick, on leave, at training. Whoever books a visit is shown it, as with an overlap, but it does not refuse the booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeOff"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/time-off/{time_off_id}":{"get":{"operationId":"getTimeOff","summary":"Get time off","tags":["Field Work"],"description":"Returns one person's time off with the scheduled visits they are on in those days.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"time_off_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"Days a staff member is out: sick, on leave, at training. Whoever books a visit is shown it, as with an overlap, but it does not refuse the booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeOff"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateTimeOff","summary":"Change or withdraw time off","tags":["Field Work"],"description":"Changes the days or the reason, or withdraws it (is_active false) when the person comes in after all. Needs the same as recording it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTimeOffInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"time_off_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"Days a staff member is out: sick, on leave, at training. Whoever books a visit is shown it, as with an overlap, but it does not refuse the booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeOff"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/subscriptions":{"get":{"operationId":"listSubscriptions","summary":"List care plans and auto-ships","tags":["Subscriptions"],"description":"Returns subscriptions, oldest first, with what each cycle includes and when the next is due. Filter by customer, status or kind, or to those with an order that can be made by a date. Subscriptions at a location are all shown to a key holding sales.view or schedule.manage there, and to no other key; filtered to one customer, all of theirs are shown.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's subscriptions. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["active","paused","cancelled"]},"in":"query","name":"status","required":false,"description":"Return only subscriptions in this status."},{"schema":{"type":"string","enum":["service_visit","shipment"]},"in":"query","name":"kind","required":false,"description":"Return only service visits or only shipments."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"orderable_by","required":false,"description":"Return only active ones whose next order can be made on or before this date."}],"responses":{"200":{"description":"One page of subscriptions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createSubscription","summary":"Start a care plan or auto-ship","tags":["Subscriptions"],"description":"Sets up a standing arrangement for a customer: what each cycle includes, how often, from when. Each cycle becomes an ordinary sales order, made from lead_days before it is due with makeSubscriptionOrder or makeDueSubscriptionOrders. Needs subscription.manage at the plan's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionInput"}}}},"responses":{"201":{"description":"A standing arrangement to supply a customer on a schedule: a recurring service visit, or a regular shipment of supplies. Each cycle becomes a sales order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/subscriptions/{subscription_id}":{"get":{"operationId":"getSubscription","summary":"Get a subscription","tags":["Subscriptions"],"description":"Returns one subscription with its lines, its schedule, the orders made for its cycles, and next. Like getSalesOrder it serves the customer in front of the clerk, so it is not limited by location.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscription_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A standing arrangement to supply a customer on a schedule: a recurring service visit, or a regular shipment of supplies. Each cycle becomes a sales order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateSubscription","summary":"Pause, resume, cancel or change a subscription","tags":["Subscriptions"],"description":"Changes the fields sent. status paused stops new orders until it is active again, and resuming skips the cycles missed meanwhile; cancelled ends it. The rhythm and start date are fixed once a cycle has been ordered. Needs subscription.manage at the plan's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSubscriptionInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscription_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A standing arrangement to supply a customer on a schedule: a recurring service visit, or a regular shipment of supplies. Each cycle becomes a sales order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/subscriptions/{subscription_id}/lines":{"post":{"operationId":"setSubscriptionLines","summary":"Change what each cycle includes","tags":["Subscriptions"],"description":"Sets the items sent on the subscription. Orders already made keep what they had. Needs subscription.manage at the plan's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetSubscriptionLinesInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscription_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A standing arrangement to supply a customer on a schedule: a recurring service visit, or a regular shipment of supplies. Each cycle becomes a sales order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/subscriptions/{subscription_id}/orders":{"post":{"operationId":"makeSubscriptionOrder","summary":"Make the order for a subscription's next cycle","tags":["Subscriptions"],"description":"Makes the open sales order for the cycle named, which must be next_due_on, from lead_days before it is due. Safe to repeat: asked again for the same cycle it returns the order already made. Then book the visit or pick the goods, hand them over and take payment on the order. Needs subscription.manage at the plan's location.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeSubscriptionOrderInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscription_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"One quote or order: what a customer has been offered or has agreed to buy, at what price. It lives through quoting, ordering, handing over and payment as one record. schema.org: Order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesOrder"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/subscriptions/{subscription_id}/skip-missed":{"post":{"operationId":"skipMissedCycles","summary":"Skip cycles that were missed","tags":["Subscriptions"],"description":"Moves an active subscription past every cycle that fell due before today without an order, when those cycles should not be supplied: missed visits and shipments are not billed in arrears. To supply them instead, run makeDueSubscriptionOrders. Needs subscription.manage at the plan's location.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscription_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A standing arrangement to supply a customer on a schedule: a recurring service visit, or a regular shipment of supplies. Each cycle becomes a sales order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/subscriptions/due-orders":{"post":{"operationId":"makeDueSubscriptionOrders","summary":"Make every subscription order that is ready","tags":["Subscriptions"],"description":"The daily run: for every active subscription, makes the order for each cycle that can be made today (due within its lead_days, or overdue and never ordered), and reports what it made and any it could not, with the reason. Safe to run as often as you like: a cycle is ordered once. Needs subscription.manage, and makes and reports only the plans at the locations where it is held.","responses":{"200":{"description":"What a run of the subscriptions made.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionOrdersMade"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/water-bodies":{"get":{"operationId":"listWaterBodies","summary":"Find customers' water","tags":["Water Care"],"description":"Returns the hot tubs, swim spas and pools whose water the business looks after, each with its last test. Filter by customer or search by name.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's water. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of water bodies.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterBodyPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createWaterBody","summary":"Record a customer's water","tags":["Water Care"],"description":"Records a body of water to test: whose, what it is, how many gallons and how it is sanitized. Doses need the volume.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWaterBodyInput"}}}},"responses":{"201":{"description":"One body of water a customer has: the water in a hot tub, a swim spa or a pool. Tests and their history belong to it. Usually it is the water in one piece of equipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterBody"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/water-bodies/{water_body_id}":{"get":{"operationId":"getWaterBody","summary":"Get a customer's water","tags":["Water Care"],"description":"Returns one body of water with its last test and what that test said to add. listWaterTests gives its history.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"water_body_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One body of water a customer has: the water in a hot tub, a swim spa or a pool. Tests and their history belong to it. Usually it is the water in one piece of equipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterBody"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateWaterBody","summary":"Update a customer's water","tags":["Water Care"],"description":"Changes the fields sent: volume, sanitizer, notes. Set is_active to false when the customer no longer has it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWaterBodyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"water_body_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One body of water a customer has: the water in a hot tub, a swim spa or a pool. Tests and their history belong to it. Usually it is the water in one piece of equipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterBody"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/water-bodies/{water_body_id}/tests":{"post":{"operationId":"recordWaterTest","summary":"Test the water and say what to add","tags":["Water Care"],"description":"Records the readings of one test and answers with what to add, in order: for each reading out of its target range, the product from the business's dosing rules, how much for this water's volume, and which size to sell and how many (item_id and quantity). Readings not taken are left out. The products can then be sold with createSalesOrder or recordCounterSale.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordWaterTestInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"water_body_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"201":{"description":"One test of a body of water: when, by whom, and what each reading was. A reading that was not taken is NULL. The tests of a water body in date order are its history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterTest"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/water-tests":{"get":{"operationId":"listWaterTests","summary":"Read a water's test history","tags":["Water Care"],"description":"Returns water tests in the order they were tested, earliest first, each with what it said to add. Filter to one body of water for its history.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"water_body_id","required":false,"description":"Return only tests of this water."}],"responses":{"200":{"description":"One page of water tests.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterTestPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/water-tests/{water_test_id}":{"get":{"operationId":"getWaterTest","summary":"Get a water test","tags":["Water Care"],"description":"Returns one water test with its readings and what it said to add.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"water_test_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One test of a body of water: when, by whom, and what each reading was. A reading that was not taken is NULL. The tests of a water body in date order are its history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaterTest"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/lab-imports":{"post":{"operationId":"importWaterLabFile","summary":"Take up a water testing lab's export","tags":["Water Care"],"description":"Reads the file a store's water testing lab exported and takes up each sample in it. A sample whose customer the lab names by an exact key (its own customer id remembered from an earlier match, a phone number or an email on file) and whose water can be told is recorded as a water test at once, with what to add. The rest wait as unmatched for matchLabSample. Rows that cannot be read are listed under rejected with why; a sample already taken up is listed under duplicates and not added again, so the same file, or an export overlapping the last, can be sent again safely.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportWaterLabFileInput"}}}},"responses":{"201":{"description":"One file or batch of readings a water testing lab sent: an export uploaded at the counter, or results an integration posted. Kept exactly as received, so a sample can be checked against what the lab said.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabImport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/lab-imports/results":{"post":{"operationId":"importWaterLabResults","summary":"Send a water testing lab's results","tags":["Water Care"],"description":"For an integration or agent that has read a lab's results already: the same as importWaterLabFile, with each sample's readings sent in our units. Use one source key per lab, so its sample ids and customer ids are recognized when sent again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportWaterLabResultsInput"}}}},"responses":{"201":{"description":"One file or batch of readings a water testing lab sent: an export uploaded at the counter, or results an integration posted. Kept exactly as received, so a sample can be checked against what the lab said.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabImport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/lab-samples":{"get":{"operationId":"listLabSamples","summary":"List samples from the water testing lab","tags":["Water Care"],"description":"Returns lab samples, newest first: by default those waiting for staff to say whose water they are. Each recorded one carries its water test and what to add.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"default":"unmatched","type":"string","enum":["unmatched","recorded","discarded"]},"in":"query","name":"status","required":false,"description":"Return samples in this state."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Return only samples from this store's lab. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only this customer's samples. Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One page of lab samples.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabSamplePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/lab-samples/{lab_sample_id}":{"get":{"operationId":"getLabSample","summary":"Get a sample from the water testing lab","tags":["Water Care"],"description":"Returns one lab sample: what the lab said, whose water it is once known, and the water test it became with what to add.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"lab_sample_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One sample a water testing lab tested: who the lab says it is from, and its readings in our units. Matched to a customer's water by exact keys when it comes in, or by staff, it becomes a water test with what to add. Readings are copied as the lab sent them; correcting one is a new test.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabSample"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/lab-samples/{lab_sample_id}/match":{"post":{"operationId":"matchLabSample","summary":"Say whose water a lab sample is","tags":["Water Care"],"description":"Records an unmatched sample as a test of the customer's water named, and answers with what to add. The lab's id for the customer is remembered, so their next sample matches by itself. A customer's water not on file is recorded first with createWaterBody.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchLabSampleInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"lab_sample_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One sample a water testing lab tested: who the lab says it is from, and its readings in our units. Matched to a customer's water by exact keys when it comes in, or by staff, it becomes a water test with what to add. Readings are copied as the lab sent them; correcting one is a new test.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabSample"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/lab-samples/{lab_sample_id}/discard":{"post":{"operationId":"discardLabSample","summary":"Throw out a lab sample","tags":["Water Care"],"description":"Throws out a sample that will not be recorded: the lab's own check, a spoiled sample, one nobody claims. It is kept, so the same sample sent again is not taken up.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscardLabSampleInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"lab_sample_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"One sample a water testing lab tested: who the lab says it is from, and its readings in our units. Matched to a customer's water by exact keys when it comes in, or by staff, it becomes a water test with what to add. Readings are copied as the lab sent them; correcting one is a new test.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabSample"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/target-ranges":{"get":{"operationId":"listTargetRanges","summary":"List where each reading should sit","tags":["Water Care"],"description":"Returns the business's target range for each reading, overall and by kind of water.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."}],"responses":{"200":{"description":"One page of target ranges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TargetRangePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"setTargetRange","summary":"Set where a reading should sit","tags":["Water Care"],"description":"Sets the range one reading should sit in, for every kind of water or one kind. Setting it again replaces it. Treatments aim for the middle. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTargetRangeInput"}}}},"x-required-permission":"policy.manage","responses":{"200":{"description":"Where one reading should sit. A reading outside its range gets a treatment. A tenant starts with common ranges and adjusts them to the product lines it sells.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TargetRange"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/dosing-rules":{"get":{"operationId":"listDosingRules","summary":"List how each reading is moved","tags":["Water Care"],"description":"Returns the business's dosing rules: which product moves which reading, and how far, as the label states.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of dosing rules.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DosingRulePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createDosingRule","summary":"Add a dosing rule","tags":["Water Care"],"description":"Records how one product moves one reading, read off its label: this amount in this many gallons moves it by this much. One active rule per reading and direction: retire the old one first. Send packs, the sizes the product is sold in, and a test names the smallest that covers its dose. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDosingRuleInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"How the tenant moves one reading up or down: which product, and how far a given amount moves it in a given volume. Read off the product's label: \"1 oz per 500 gallons raises pH by 0.2\". One active rule per reading and direction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DosingRule"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/water_care/dosing-rules/{dosing_rule_id}":{"patch":{"operationId":"updateDosingRule","summary":"Change a dosing rule","tags":["Water Care"],"description":"Changes the fields sent on a rule, or retires it with is_active false. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDosingRuleInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"dosing_rule_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"How the tenant moves one reading up or down: which product, and how far a given amount moves it in a given volume. Read off the product's label: \"1 oz per 500 gallons raises pH by 0.2\". One active rule per reading and direction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DosingRule"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/books/check":{"get":{"operationId":"getBooksCheck","summary":"Check the books are consistent","tags":["Books"],"description":"Checks every rule that must hold across the business's books and says which fail. Run it after a sequence of changes that moved money or stock. A failing rule is a defect in the system: no request can cause one, so report it with the rule names rather than trying to correct it. Requires the cost.view permission.","x-required-permission":"cost.view","responses":{"200":{"description":"Everything that must be true of the business's books at once: control accounts equal what they summarize, running totals equal their ledgers, the journal balances. No request can make a rule fail, so a failing rule is a defect in the system, not in the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BooksCheck"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/accounts":{"get":{"operationId":"listLedgerAccounts","summary":"List the chart of accounts","tags":["Books"],"description":"Returns the business's ledger accounts, oldest first, with balances for a key that holds cost.view. Accounts with a posting_role are the ones the system posts to; control accounts take no manual entries.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"query","name":"search","required":false,"description":"Words to look for in names, numbers, emails and phone numbers. Every word must be found, in any order; case and a plural ending do not matter."},{"schema":{"type":"string","enum":["asset","liability","equity","income","cost_of_goods_sold","expense"]},"in":"query","name":"kind","required":false,"description":"Return only accounts of this kind."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_bank_account","required":false,"description":"true: only the accounts marked as bank accounts. false: only the others."}],"responses":{"200":{"description":"One page of the chart of accounts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LedgerAccountPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createLedgerAccount","summary":"Add a ledger account","tags":["Books"],"description":"Adds an account to the chart, for example a clearing account for a financing company. Codes and names are unique; kind is fixed once the account exists. Mark a real bank account with is_bank_account: store managers then bring change from it and bank into it with cash.manage alone. With more than one bank account, mark the one the business writes its checks on with writes_checks: refunds by check come out of it. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLedgerAccountInput"}}}},"x-required-permission":"ledger.post","responses":{"201":{"description":"One account in the tenant's chart of accounts. Every journal line debits or credits one. A new tenant starts with the accounts the system itself posts to and adds its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LedgerAccount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/accounts/{ledger_account_id}":{"patch":{"operationId":"updateLedgerAccount","summary":"Update a ledger account","tags":["Books"],"description":"Renames or recodes an account, or retires it with is_active false. Only an account with a zero balance can be retired, and never one the system posts to. Marks a bank account (is_bank_account), and the one the business writes its checks on (writes_checks). Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLedgerAccountInput"}}}},"parameters":[{"schema":{"type":"string","x-natural-key":"code"},"in":"path","name":"ledger_account_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time). Accepts the record's id or its account code."}],"x-required-permission":"ledger.post","responses":{"200":{"description":"One account in the tenant's chart of accounts. Every journal line debits or credits one. A new tenant starts with the accounts the system itself posts to and adds its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LedgerAccount"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/journal-entries":{"get":{"operationId":"listJournalEntries","summary":"Read the journal","tags":["Books"],"description":"Returns journal entries, oldest first, each with its debits and credits. Filter by date, by an account the entry touches, or to manual entries only. Every event that moves money or stock posts one; source_table and source_id say which. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date_from","required":false,"description":"Return only entries dated on or after this date."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date_to","required":false,"description":"Return only entries dated on or before this date."},{"schema":{"type":"string","x-natural-key":"code"},"in":"query","name":"ledger_account_id","required":false,"description":"Return only entries with a line on this account. Accepts the record's id or its account code."},{"schema":{"type":"string","maxLength":64},"in":"query","name":"source_table","required":false,"description":"Return only entries the system posted for this kind of record, for example payment."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"manual","required":false,"description":"true: only entries people posted. false: only the system's."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of journal entries and the cursor for the next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"postJournalEntry","summary":"Post a manual journal entry","tags":["Books"],"description":"Posts an accounting entry by hand, for what no event in the system records: processing fees, a correction between accounts. Debits must equal credits, control accounts are refused (change the records behind them instead), and nothing can be dated in a closed period. A retry with the same idempotency_key returns the entry already posted. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostJournalEntryInput"}}}},"x-required-permission":"ledger.post","responses":{"201":{"description":"One accounting entry: a dated set of debits and credits that add up to the same amount. Entries are only ever added. The system posts one for each event that moves money or stock value, and people post manual ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/journal-entries/{journal_entry_id}":{"get":{"operationId":"getJournalEntry","summary":"Get a journal entry","tags":["Books"],"description":"Returns one entry with its lines, what caused it, and whether it has been reversed. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"journal_entry_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One accounting entry: a dated set of debits and credits that add up to the same amount. Entries are only ever added. The system posts one for each event that moves money or stock value, and people post manual ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/journal-entries/{journal_entry_id}/reverse":{"post":{"operationId":"reverseJournalEntry","summary":"Reverse a manual journal entry","tags":["Books"],"description":"Cancels a manual entry, line for line, with a new entry. An entry the system posted for an event is corrected by correcting the event (a return, a refund), not here. Returns the reversing entry, so it also needs cost.view. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReverseJournalEntryInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"journal_entry_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"ledger.post","responses":{"201":{"description":"One accounting entry: a dated set of debits and credits that add up to the same amount. Entries are only ever added. The system posts one for each event that moves money or stock value, and people post manual ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/sales":{"get":{"operationId":"getSalesReport","summary":"Report sales for a period, grouped as asked, against another period","tags":["Reports"],"description":"Answers how the business sold: by store, period, category, brand, item, salesperson, channel or customer (group_by, and then_by within it), with totals. basis delivered (the default) counts hand-overs and returns on the day goods changed hands, as the books do: a sale belongs to the store that made it, whichever store the goods left from, so a store's revenue is the income in its books. basis booked counts orders when the customer agreed, less cancellations, and gives the backlog sold but not delivered. The period defaults to the month to date; compare sets it against the period before or a year earlier. A sale credited to several salespeople is split by their shares; without commission.view a key sees its own row and everyone_else. Costs and margins need cost.view where the sales were made. Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on. The rows behind a figure are in listSales.","parameters":[{"schema":{"default":"delivered","type":"string","enum":["delivered","booked"]},"in":"query","name":"basis","required":false,"description":"delivered (the default): sales when the goods changed hands, as the books count them. booked: orders when the customer agreed, less cancellations, with the backlog not yet delivered."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"The first day counted. Defaults to the first of the month `to` falls in."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"The last day counted. Defaults to today, at location_id when one is named, else where the business is."},{"schema":{"default":"none","type":"string","enum":["none","prior_period","prior_week","prior_year","prior_year_weekday"]},"in":"query","name":"compare","required":false,"description":"What to set the figures against. prior_period: the period just before, of the same length; a period that starts on the first of a month is set against as many months before, whole months against whole months and a month (or months) to date against the same days of the months before. prior_week: the same days a week earlier, so yesterday is set against the same weekday last week; for a period of a week or less. prior_year: the same dates a year earlier, a period ending on the last of a month against the same month's end. prior_year_weekday: the same weekdays a year earlier, 52 weeks (364 days) back, so a Saturday is set against a Saturday; for a day, a week or a few weeks, where the weekday matters more than the date. Grouped by day or week, the compared period has no more days than this one and each day is set against the day as far into it."},{"schema":{"type":"string","enum":["day","week","month","quarter","year","location","category","top_category","brand","item","salesperson","channel","customer","order","tracking"]},"in":"query","name":"group_by","required":false,"description":"Group the rows by this. Omit for totals only."},{"schema":{"type":"string","enum":["day","week","month","quarter","year","location","category","top_category","brand","item","salesperson","channel","customer","order","tracking"]},"in":"query","name":"then_by","required":false,"description":"Split each group by this as well, for example group_by=location&then_by=month."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Count only sales this store made: its own orders, wherever the goods left from. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"category_id","required":false,"description":"Count only items filed under this category or any below it."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"brand_id","required":false,"description":"Count only items of this brand."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Count only this item. Accepts the record's id or its SKU."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Count only this salesperson's share of the sales they are credited on. Another person's needs commission.view."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"sales_channel_id","required":false,"description":"Count only orders that came through this channel."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Count only this customer's purchases. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","enum":["none","quantity","serial"]},"in":"query","name":"tracking","required":false,"description":"Count only items tracked this way: serial for units such as spas, quantity for stock, none for services."},{"schema":{"default":100,"type":"integer","minimum":1,"maximum":1000},"in":"query","name":"limit","required":false,"description":"The most rows to return. The rest are added up in other."}],"responses":{"200":{"description":"Sales for a period, grouped as asked, with totals and what they are set against.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesReport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/day-close":{"get":{"operationId":"getDayClose","summary":"Close a store's day: sales, money by method, checks, drawers and safe","tags":["Reports"],"description":"Answers whether each store's money for a day is accounted for: its sales, what was taken and given back by each payment method and what that money was for (orders, on account, gift cards, returns), with store credit, account credit, gift cards spent and financing apart as value moved, what was taken and used on deposit, every check for the deposit slip, each drawer with what it should have held and how far over or short it was, the safe from opening to closing balance with its trips to the bank, and exceptions with what to call next: drawers still open, drawers waiting for a manager, cash taken or given back without a drawer. Everything is dated at the store. Needs cash.manage at each store shown, because it shows what drawers should hold; without location_id it covers every store where the key holds it. Sales need sales.view there, costs cost.view.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"date","required":false,"description":"The business day to close. Defaults to today at each store."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"The store to close. Omit for every store where this key holds cash.manage. Accepts the record's id or its branch code."}],"responses":{"200":{"description":"Whether each store's money for a day is accounted for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DayClose"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/financial-statement":{"get":{"operationId":"getFinancialStatement","summary":"Get a profit and loss, balance sheet or trial balance of the books","tags":["Reports"],"description":"Reads the journal live. profit_and_loss: income, cost of goods sold and gross profit for a period, then the expenses posted here; for one store (location_id) or with a column per store (by_location), against the period before or a year earlier. It stops near gross profit: payroll, rent and other bills are in the accounting system (unposted_note). balance_sheet: assets, liabilities and equity at `to`, with earnings of earlier fiscal years and of this one worked out. trial_balance: each account's opening, debits, credits and closing, to tie to the accounting system; within one fiscal year. Fiscal years start in the business's fiscal_year_start_month (getPolicy). Needs cost.view, everywhere unless location_id names the store it is held at.","parameters":[{"schema":{"default":"profit_and_loss","type":"string","enum":["profit_and_loss","balance_sheet","trial_balance"]},"in":"query","name":"statement","required":false,"description":"Which statement."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"The first day counted. Defaults to the first of the month `to` falls in."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"The last day counted. Defaults to today, at location_id when one is named, else where the business is."},{"schema":{"default":"none","type":"string","enum":["none","prior_period","prior_week","prior_year","prior_year_weekday"]},"in":"query","name":"compare","required":false,"description":"What to set the figures against. prior_period: the period just before, of the same length; a period that starts on the first of a month is set against as many months before, whole months against whole months and a month (or months) to date against the same days of the months before. prior_week: the same days a week earlier, so yesterday is set against the same weekday last week; for a period of a week or less. prior_year: the same dates a year earlier, a period ending on the last of a month against the same month's end. prior_year_weekday: the same weekdays a year earlier, 52 weeks (364 days) back, so a Saturday is set against a Saturday; for a day, a week or a few weeks, where the weekday matters more than the date. Grouped by day or week, the compared period has no more days than this one and each day is set against the day as far into it."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"For a profit and loss, only what is posted to this store. Needs cost.view there. Accepts the record's id or its branch code."},{"schema":{"default":"false","type":"string","enum":["true","false"]},"in":"query","name":"by_location","required":false,"description":"For a profit and loss, true to split every figure by store, with a column for amounts posted to none."},{"schema":{"default":"false","type":"string","enum":["true","false"]},"in":"query","name":"include_zero","required":false,"description":"true to list active accounts with nothing on them as well."}],"responses":{"200":{"description":"A profit and loss, a balance sheet or a trial balance of the accounts Retavon keeps, read live from the journal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancialStatement"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/receivables-aging":{"get":{"operationId":"getReceivablesAging","summary":"Get what customers owe by how many days past due","tags":["Receivables"],"description":"Answers who owes the business and how late it is: per customer (the default), per location or in one row, what is owed in each bucket of days past due (getPolicy, receivable_aging_bucket_days: current, 1-30, 31-60, 61-90 and 91+ unless the business chose others). Counts orders with goods handed over and not paid for, and what is owed on account; together they are Accounts Receivable, which the totals equal. Per customer it adds their credit limit, whether goods on account are held from them, and the deposits and credit they hold. As of today only: a statement (issueStatement) ages an account at the end of a period. Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on. Filtered to one customer, everything they owe is shown. A customer's limit, hold, deposits and credit are theirs at every store, as getCustomer shows them. The debts behind a row are in listReceivables.","parameters":[{"schema":{"default":"customer","type":"string","enum":["customer","location","none"]},"in":"query","name":"group_by","required":false,"description":"A row per customer, per location, or none (one row)."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only orders made at this location, and debts on account recorded there. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Only what this customer owes. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"default":"false","type":"string","enum":["true","false"]},"in":"query","name":"overdue_only","required":false,"description":"true for only rows with something past due."},{"schema":{"default":100,"type":"integer","minimum":1,"maximum":1000},"in":"query","name":"limit","required":false,"description":"The most rows to list; the rest are added up in other."}],"responses":{"200":{"description":"What customers owe, by how many days past due, in the business's buckets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceivablesAging"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/customer-liabilities":{"get":{"operationId":"getCustomerLiabilities","summary":"Get what the business owes customers: gift cards, credit and deposits","tags":["Reports"],"description":"Answers how much the business owes its customers and how it moved: what gift cards hold, store credit, money paid on account and not yet applied, and deposits on goods not yet handed over. Each is added up from its own records and set beside its account in the journal, which it equals, with what was added and taken off in the period (default the month to date), how many hold some, and how much has sat unused for dormant_days. Gift cards are also shown by status, with what may be taken as breakage under the business's policy. For the whole business: a customer's credit can be spent at any store. Needs cost.view everywhere, as the books do. Never shows a gift card's code.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"The first day counted. Defaults to the first of the month `to` falls in."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"The last day counted. Defaults to today, at location_id when one is named, else where the business is."},{"schema":{"default":365,"type":"integer","minimum":1,"maximum":3650},"in":"query","name":"dormant_days","required":false,"description":"Days without activity after which what a holder has counts as dormant."}],"responses":{"200":{"description":"What the business owes its customers in gift cards, store credit, account credit and deposits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerLiabilities"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/customers-due":{"get":{"operationId":"getCustomersDue","summary":"Find customers due to buy filters, chemicals or other care items again","tags":["Reports"],"description":"Answers who is due or overdue for something they buy again and again: a category (with those below it), one item, or with neither anything they buy. Each item is timed on its own: bought on several days, it is due the median gap between them after the last; bought once, after its category's repurchase_days (updateCategory); with neither it is not timed. A customer is due for whichever item comes due first. Purchases count from the last two years, kept goods only, never anonymous sales. Each customer comes with their home store (where they last bought it), what they usually buy, what they spent, whether a subscription already covers it, and each contact point with whether their consent allows a marketing message, which a reminder is: send only what it allows (sendMessage). Counts by status and the share of last year's buyers who came back are added. Rows at a location are all shown to a key that holds sales.view there; elsewhere a key sees only those on orders its staff member is credited on.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"category_id","required":false,"description":"Customers who buy from this category or any below it."},{"schema":{"type":"string","x-natural-key":"sku"},"in":"query","name":"item_id","required":false,"description":"Customers who buy this item, instead of a category. With neither, anything they buy. Accepts the record's id or its SKU."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only customers whose home store this is. Accepts the record's id or its branch code."},{"schema":{"default":"overdue","type":"string","enum":["due_soon","overdue","lapsed","due_or_overdue","all"]},"in":"query","name":"status","required":false,"description":"Which customers to list: due_soon, overdue, lapsed, due_or_overdue, or all."},{"schema":{"default":14,"type":"integer","minimum":0,"maximum":365},"in":"query","name":"due_within_days","required":false,"description":"How many days ahead counts as due soon."},{"schema":{"default":2,"type":"integer","minimum":2,"maximum":20},"in":"query","name":"lapsed_after","required":false,"description":"How many of their intervals since the last purchase before a customer counts as lapsed."},{"schema":{"default":1,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"min_purchases","required":false,"description":"Only customers who bought it on at least this many days."},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"The most customers to return."},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"next_cursor from the previous page."}],"responses":{"200":{"description":"Customers due to buy something again, from what they bought and how often.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomersDue"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/stock-performance":{"get":{"operationId":"getStockPerformance","summary":"Get stock on hand with its value, turns, days of supply and dead stock","tags":["Reports"],"description":"Answers how stock is earning its keep: per item (or per item and location with by_location), units and value on hand, units sold and what they cost over the last window_days, turns a year at that pace, days of supply, when stock last came in and last sold, and whether it is dead (none sold in not_sold_days). Sales count where the goods left from. Totals cover every matching row, and with no narrowing but location_id the value equals the Inventory account. Values, costs and turns need cost.view where the stock is. Spa units by age are in getStockAging.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only stock at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"category_id","required":false,"description":"Only items in this category or below it."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"brand_id","required":false,"description":"Only items of this brand."},{"schema":{"type":"string","enum":["quantity","serial"]},"in":"query","name":"tracking","required":false,"description":"How stock is kept. none: not counted. quantity: a count per location. serial: every unit is recorded individually by serial number, as for a hot tub. Only a good can be tracked. Fixed once the item exists."},{"schema":{"default":90,"type":"integer","minimum":7,"maximum":730},"in":"query","name":"window_days","required":false,"description":"The days sales are counted over."},{"schema":{"default":180,"type":"integer","minimum":1,"maximum":3650},"in":"query","name":"not_sold_days","required":false,"description":"The days without a sale that make stock dead."},{"schema":{"default":"all","type":"string","enum":["all","dead","negative"]},"in":"query","name":"only","required":false,"description":"all, dead (dead stock only) or negative (below zero on hand)."},{"schema":{"default":"value","type":"string","enum":["value","turns","days_of_supply"]},"in":"query","name":"sort","required":false,"description":"value: most value first. turns: slowest first. days_of_supply: most days first, none sold first."},{"schema":{"default":"false","type":"string","enum":["true","false"]},"in":"query","name":"by_location","required":false,"description":"true for a row per item and location."},{"schema":{"default":100,"type":"integer","minimum":1,"maximum":1000},"in":"query","name":"limit","required":false,"description":"The most rows to list."}],"responses":{"200":{"description":"Stock on hand with its value, sales pace, turns, days of supply and dead stock.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockPerformanceReport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/replenishment":{"get":{"operationId":"getReplenishment","summary":"Get what to order before it runs out, in vendor packs, by vendor","tags":["Reports"],"description":"Answers what to reorder: for each item stocked or kept at a par level at premises, units a day expected to go (basis recent: the last window_days; last_year: the same coming cover_days a year ago), counting what its vans used, and what to order to cover cover_days beside what is committed, on hand and on order. An item fallen to its minimum (or below par) is brought back up to par even when it sells too slowly to need it; reasons says which applied. Rounded up to the preferred vendor's packs, grouped by vendor with what each order would cost and what other locations could transfer. Vans are never ordered for: transfers lists what each needs from its home to reach par. Order with createPurchaseOrder or addPurchaseOrderLines; move stock with transferStockBatch. Costs need cost.view at the location.","parameters":[{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only what this location needs. Accepts the record's id or its branch code."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"vendor_id","required":false,"description":"Only items whose preferred vendor this is."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"category_id","required":false,"description":"Only items in this category or below it."},{"schema":{"default":"quantity","type":"string","enum":["quantity","serial"]},"in":"query","name":"tracking","required":false,"description":"quantity (the default) or serial: serialized units are ordered one by one."},{"schema":{"default":"recent","type":"string","enum":["recent","last_year"]},"in":"query","name":"basis","required":false,"description":"Where the daily rate comes from."},{"schema":{"default":28,"type":"integer","minimum":7,"maximum":365},"in":"query","name":"window_days","required":false,"description":"For basis recent, the days of sales the rate is taken from."},{"schema":{"default":30,"type":"integer","minimum":1,"maximum":365},"in":"query","name":"cover_days","required":false,"description":"The days the order is to cover."}],"responses":{"200":{"description":"What to order before it runs out, rounded to packs, by vendor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Replenishment"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/stock-shrink":{"get":{"operationId":"getStockShrink","summary":"Get stock lost, damaged or found, by store and reason","tags":["Reports"],"description":"Answers what was lost and why: units and cost of stock adjusted, written down, or found over or short on a count in the period, by location and by the business's reasons (broken, expired, theft, count error, ...), set against the cost of what was sold from the same locations. The journal entries those movements posted are added up by account, which ties the total to Inventory and to Inventory Adjustments. Customer returns not put back on the shelf are shown apart: their cost stays in cost of goods sold. Defaults to this month. Costs need cost.view at every location counted.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"The first day counted. Defaults to the first of the month."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"The last day counted. Defaults to today."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only stock at this location. Accepts the record's id or its branch code."},{"schema":{"type":"string","x-natural-key":"code"},"in":"query","name":"adjustment_reason_id","required":false,"description":"Only stock lost or found for this reason, by its code or id. Accepts the record's id or its reason code."}],"responses":{"200":{"description":"Stock lost, damaged or found in a period, by location and reason, against the cost of what was sold.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockShrink"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/reports/service-workload":{"get":{"operationId":"getServiceWorkload","summary":"Get open work orders by age, visits per day, and care plans coming due","tags":["Reports"],"description":"Answers how loaded the service side is: work orders open now, paid and warranty, by days open (0-7, 8-14, 15-30, 31-60, 61+) with the longest open and whether a visit is booked; deliveries, service calls and consultations per store and day from from to to (default the next 14 days) with the hours booked, the people on them and visits with no one on them; and care plan visits and auto-ship cycles coming due, with those already overdue. Days are each store's own.","parameters":[{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"from","required":false,"description":"The first day. Defaults to today."},{"schema":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"in":"query","name":"to","required":false,"description":"The last day. Defaults to 13 days after from. At most 92 days."},{"schema":{"type":"string","x-natural-key":"branch_code"},"in":"query","name":"location_id","required":false,"description":"Only this store. Accepts the record's id or its branch code."}],"responses":{"200":{"description":"Open work orders by age, visits per day against people, and subscription cycles coming due.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceWorkload"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment-methods":{"get":{"operationId":"listPaymentMethods","summary":"List payment methods","tags":["Settings"],"description":"Returns the ways customers can pay, oldest first, each with the account its money lands in. Take a payment with one that is active. The one with kind store_credit spends a customer's store credit, and a refund to it gives store credit instead of money. The one with kind account_credit (On account) spends money the customer paid on account (applyAccountCredit), and a refund to it puts money held on an order back into their account credit. The one with kind gift_card spends a gift card, naming it by its code, and a refund to it puts money back onto a card.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of payment methods.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createPaymentMethod","summary":"Add a payment method","tags":["Settings"],"description":"Adds a way customers pay, naming the account its money lands in. Add the account first with createLedgerAccount if it does not exist. A lender the business sells on is added with createFinancingLender instead, which gives it its own method that holds what the lender owes until it pays out. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentMethodInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A way customers pay the tenant, for example Cash, \"Card (Stripe)\" or \"Wells Fargo financing\". Each names the account its money lands in, so the books show where every payment went. A new tenant has Cash, Check, Card, Store credit, On account, Gift card and Trade-in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment-methods/{payment_method_id}":{"patch":{"operationId":"updatePaymentMethod","summary":"Update a payment method","tags":["Settings"],"description":"Changes the fields sent. Set is_active to false to stop offering it; payments already taken with it stay. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentMethodInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"payment_method_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A way customers pay the tenant, for example Cash, \"Card (Stripe)\" or \"Wells Fargo financing\". Each names the account its money lands in, so the books show where every payment went. A new tenant has Cash, Check, Card, Store credit, On account, Gift card and Trade-in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/policy":{"get":{"operationId":"getPolicy","summary":"Get the business's policies","tags":["Settings"],"description":"Returns the rules the business has chosen for itself: how stock is costed, whether stock may go negative, through what date the books are closed, the credit limit and payment terms new customers start with, when overdue accounts are held and what paying late is charged, how sales tax is rounded, which day's rates it is charged at, whether goods carried out of a location in another tax area are refused or taxed there, whether it gives store credit, whether and how it takes goods back without a receipt, the cancellation and restocking fees it keeps, how long a quote's prices hold and whether a quote holds the units it names, whether refunds go back only by the methods an order was paid with, the deposit it takes before an order opens, whether a price below cost needs order.discount, and how deliveries are run: the site readiness and delivery checklists, whether a sign-off is required, reminders, and what a failed delivery is charged; and how service work is charged: the labor item, rounding and minimum, travel, signatures, the default labor cost rate, and how far back a device's records are taken.","responses":{"200":{"description":"The business rules a tenant chooses for itself, one row per tenant, created with the tenant. Each rule is a typed column so the database can enforce it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updatePolicy","summary":"Change the business's policies","tags":["Settings"],"description":"Changes the policies sent. Costing, negative stock, stock of an unknown lot (unknown_lot_allowed) and the tax choices need policy.manage. Rounding and rate date apply to orders written up after the change, and carry_out_elsewhere to every hand-over from then on. The costing method is fixed once any stock has moved. Closing the books through a date needs books.close and stops anything being posted on or before it; set it back to reopen. The credit limit and payment terms new customers start with, when overdue accounts are held, and finance charges for paying late need credit.manage, and so does the deposit taken before an order opens (min_deposit_percent). Store credit, returns without a receipt, fees, the return window, quote terms, refund methods, pricing below cost, package pricing, the stock and count choices, the delivery choices, the service choices, the cash choices, whether payments on account apply themselves, the gift card choices and how reports count years and weeks need policy.manage; taking goods back for store credit only needs store credit offered. Fees and quote terms apply to returns, cancellations and quotes from then on.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePolicyInput"}}}},"responses":{"200":{"description":"The business rules a tenant chooses for itself, one row per tenant, created with the tenant. Each rule is a typed column so the database can enforce it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/custom-fields":{"get":{"operationId":"listCustomFields","summary":"List the fields the business has added","tags":["Settings"],"description":"Returns the fields the business has added to records, such as seats on an item, each with its type and allowed values. Records carry their values in custom_fields; setCustomFieldValues sets them.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["appointment","customer","equipment","item","purchase_order","sales_order","serialized_unit","site","subscription","vendor","work_order"]},"in":"query","name":"table_name","required":false,"description":"Return only fields on this table."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of custom fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createCustomField","summary":"Add a field to a kind of record","tags":["Settings"],"description":"Adds a typed field the business wants on one kind of record, for example seats and voltage on items. The table, key and type are fixed once it exists. Describe it for someone who has never seen the business. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomFieldInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"The definition of one field a tenant has added to one of the shared tables, for example \"seats\" on item for a hot tub dealer. Values live in that table's custom_fields column as {key: value} and are checked against these definitions on every write.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomField"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/custom-fields/{custom_field_id}":{"patch":{"operationId":"updateCustomField","summary":"Change a field's label, description or choices","tags":["Settings"],"description":"Changes the fields sent, or retires the field with is_active false: values already stored stay. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomFieldInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"custom_field_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"The definition of one field a tenant has added to one of the shared tables, for example \"seats\" on item for a hot tub dealer. Values live in that table's custom_fields column as {key: value} and are checked against these definitions on every write.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomField"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/custom-fields/values":{"post":{"operationId":"setCustomFieldValues","summary":"Set custom field values on a record","tags":["Settings"],"description":"Sets or clears the business's own fields on one record, each value written as text and checked against the field's type. Setting them on an item or a vendor needs catalog.manage, on a serialized unit stock.adjust, on a purchase order purchase.manage at its location: what changing the record itself needs.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCustomFieldValuesInput"}}}},"responses":{"200":{"description":"A record's custom field values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldValues"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/checklists":{"get":{"operationId":"listChecklists","summary":"List checklists and their questions","tags":["Settings"],"description":"Returns the business's checklists with their questions in order: the site readiness checklist a site check answers and the one the crew answers at a delivery. Each question says what kind of answer it takes and what passes.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["site_readiness","delivery","service"]},"in":"query","name":"purpose","required":false,"description":"Return only checklists for this purpose."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of checklists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChecklistPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createChecklist","summary":"Create a checklist","tags":["Settings"],"description":"Creates a checklist with its questions: what a site check asks (site_readiness) or what the crew checks at a delivery (delivery). Make it the business's own with updatePolicy (site_readiness_checklist_id, delivery_checklist_id). Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChecklistInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A list of questions the business has someone answer on a job: whether a customer's site is ready for a delivery, or what the crew checks before leaving it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Checklist"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/checklists/{checklist_id}":{"patch":{"operationId":"updateChecklist","summary":"Change a checklist or add questions","tags":["Settings"],"description":"Changes the name or description, retires the checklist (is_active false), or adds questions at the end (add_items). Checks already recorded keep the questions they answered. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChecklistInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"checklist_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A list of questions the business has someone answer on a job: whether a customer's site is ready for a delivery, or what the crew checks before leaving it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Checklist"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/checklist-items/{checklist_item_id}":{"patch":{"operationId":"updateChecklistItem","summary":"Change or retire a checklist question","tags":["Settings"],"description":"Changes one question: its help text and position at any time, its wording and rules until a check has answered it, or retires it (is_active false). Returns its checklist. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChecklistItemInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"checklist_item_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A list of questions the business has someone answer on a job: whether a customer's site is ready for a delivery, or what the crew checks before leaving it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Checklist"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/events":{"get":{"operationId":"listEvents","summary":"Read what happened to a record","tags":["Integrations"],"description":"Returns events, oldest first: what happened, to which row, and the row as it was. Filter by the row (table_name and row_id) for its history, or by event_type. To follow the feed as it grows, use an event consumer instead. Payloads include costs, so this needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","maxLength":64},"in":"query","name":"table_name","required":false,"description":"Return only events about rows of this table."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"row_id","required":false,"description":"Return only events about this row."},{"schema":{"type":"string","enum":["customer.created","order.opened","order.completed","order.cancelled","sale.fulfilled","sale.returned","payment.received","payment.refunded","payment.reversed","appointment.scheduled","appointment.rescheduled","appointment.completed","appointment.cancelled","work_order.opened","work_order.completed","stock.received","water_test.recorded","appointment.en_route","appointment.arrived","appointment.failed","appointment.upcoming","delivery.site_not_ready","site_check.recorded","cash_drawer.opened","cash_drawer.closed","cash_drawer.reconciled","account_payment.received","account_payment.refunded","account_payment.reversed","gift_card.issued","gift_card.reloaded","gift_card.voided","gift_card.replaced","gift_card.cashed_out","gift_card.written_off","gift_card.reversed","receipt.issued","statement.issued","work_order.signed_off","message.failed","customer_reply.received","lead.created","lead.won","lead.lost"]},"in":"query","name":"event_type","required":false,"description":"Return only events of this type."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/events/{event_id}":{"get":{"operationId":"getEvent","summary":"Read one event","tags":["Integrations"],"description":"Returns one event: what happened, to which row, and the row as it was then. Payloads include costs, so this needs cost.view. Requires the cost.view permission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"event_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"Something that happened, written by the database in the same transaction as the fact itself: an order opened, goods handed over, a payment received. The feed that integrations, automations and agents react to. Read it through app.next_events(), which gives a safe order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/event-consumers":{"get":{"operationId":"listEventConsumers","summary":"List readers of the event feed","tags":["Integrations"],"description":"Returns the adapters and agents that follow the event feed, each with how far it has read. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of event consumers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventConsumerPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createEventConsumer","summary":"Start following the event feed","tags":["Integrations"],"description":"Registers a reader of the event feed under a name. It starts at the beginning: getNewEvents gives what it has not yet finished with, and acknowledgeEvents moves it on. Requires the cost.view permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEventConsumerInput"}}}},"x-required-permission":"cost.view","responses":{"201":{"description":"One reader of the event feed (an adapter, the automation runner, an agent) and the last event it has finished with. A consumer that stops and starts again carries on from there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventConsumer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/event-consumers/{event_consumer_id}/new-events":{"get":{"operationId":"getNewEvents","summary":"Get the events a reader has not finished with","tags":["Integrations"],"description":"Returns the next events after the consumer's cursor, in an order that is safe to resume from: nothing is skipped or repeated. Handle them, then acknowledgeEvents with the last one. Asking again before acknowledging returns the same events. Requires the cost.view permission.","parameters":[{"schema":{"default":100,"type":"integer","minimum":1,"maximum":1000},"in":"query","name":"limit","required":false,"description":"Most events to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"event_consumer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"Events a consumer has not yet finished with.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewEvents"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/event-consumers/{event_consumer_id}/acknowledge":{"post":{"operationId":"acknowledgeEvents","summary":"Mark events as handled","tags":["Integrations"],"description":"Moves a consumer's cursor to the last event it has finished with, by that event's id. getNewEvents then starts after it. Requires the cost.view permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcknowledgeEventsInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"event_consumer_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One reader of the event feed (an adapter, the automation runner, an agent) and the last event it has finished with. A consumer that stops and starts again carries on from there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventConsumer"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/external-links":{"get":{"operationId":"listExternalLinks","summary":"Find what a record is in an outside system","tags":["Integrations"],"description":"Returns links between rows here and records in outside systems. Look one up by the row, or by the outside id, before creating anything outside, so nothing is created twice.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","maxLength":40},"in":"query","name":"system","required":false,"description":"Return only links to this system, for example quickbooks."},{"schema":{"type":"string","maxLength":64},"in":"query","name":"table_name","required":false,"description":"Return only links for rows of this table."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"row_id","required":false,"description":"Return only links for this row."},{"schema":{"type":"string","maxLength":200},"in":"query","name":"external_id","required":false,"description":"Return only links to this outside id."}],"responses":{"200":{"description":"One page of external links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalLinkPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"setExternalLink","summary":"Record what a row is in an outside system","tags":["Integrations"],"description":"States that a row here is a record in an outside system: this customer is that QuickBooks customer. Sending it again for the same system and row replaces the outside id. Needs cost.view, as reading the event feed an integration syncs from does. Requires the cost.view permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetExternalLinkInput"}}}},"x-required-permission":"cost.view","responses":{"200":{"description":"A statement that one row here is one record in an outside system: this customer is that QuickBooks customer, this payment is that Stripe charge, this item is that web store product. An adapter looks here before creating anything outside, so nothing is created twice.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalLink"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/journal-exports":{"get":{"operationId":"listJournalExports","summary":"List batches sent to the accounting system","tags":["Integrations"],"description":"Returns journal export batches, oldest first. Filter by status to find what still needs sending. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["pending","exported","failed"]},"in":"query","name":"status","required":false,"description":"Return only batches in this status."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of export batches.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalExportPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createJournalExport","summary":"Batch journal entries for the accounting system","tags":["Integrations"],"description":"Puts every journal entry dated on or before through_date that is not already in a batch into a new batch, so nothing is exported twice and nothing is missed. Send it, then updateJournalExport. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJournalExportInput"}}}},"x-required-permission":"ledger.post","responses":{"201":{"description":"One batch of journal entries sent to the tenant's accounting system. Made by app.create_journal_export(), which puts each entry in exactly one batch, so nothing is exported twice and nothing is missed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalExport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/journal-exports/{journal_export_id}":{"patch":{"operationId":"updateJournalExport","summary":"Record what the accounting system did with a batch","tags":["Integrations"],"description":"Marks a batch exported once the accounting system has it (final), or failed with the reason, to be tried again. Requires the ledger.post permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateJournalExportInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"journal_export_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"ledger.post","responses":{"200":{"description":"One batch of journal entries sent to the tenant's accounting system. Made by app.create_journal_export(), which puts each entry in exactly one batch, so nothing is exported twice and nothing is missed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalExport"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/message-templates":{"get":{"operationId":"listMessageTemplates","summary":"List message templates","tags":["Messaging"],"description":"Returns the words the business sends customers, with places for the details, oldest first.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of message templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTemplatePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createMessageTemplate","summary":"Write a message template","tags":["Messaging"],"description":"Adds the words of a message, for one channel and purpose, with places for the details. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageTemplateInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"The words of a message the tenant sends to customers, with places for the details: \"Hi {{customer.given_name}}, your delivery is booked for {{appointment.scheduled_window}}.\"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTemplate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/message-templates/{message_template_id}":{"patch":{"operationId":"updateMessageTemplate","summary":"Change a message template","tags":["Messaging"],"description":"Changes the fields sent. Messages already queued keep their words. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageTemplateInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"message_template_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"The words of a message the tenant sends to customers, with places for the details: \"Hi {{customer.given_name}}, your delivery is booked for {{appointment.scheduled_window}}.\"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTemplate"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/automations":{"get":{"operationId":"listAutomations","summary":"List automations","tags":["Messaging"],"description":"Returns the standing instructions: when this happens, send that, after this long.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"is_active","required":false,"description":"Return only active (true) or only retired (false) records. Omit for both."}],"responses":{"200":{"description":"One page of automations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createAutomation","summary":"Automate a message","tags":["Messaging"],"description":"Sets a standing instruction: when an event of this type happens, send the customer this template, after this many minutes. runAutomations acts on it. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAutomationInput"}}}},"x-required-permission":"policy.manage","responses":{"201":{"description":"A standing instruction: when this kind of event happens, send the customer that message, after this long. \"When a delivery is scheduled, text the confirmation.\" \"Thirty days after a sale is fulfilled, email the care reminder.\"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/automations/{automation_id}":{"patch":{"operationId":"updateAutomation","summary":"Change an automation","tags":["Messaging"],"description":"Changes the fields sent, or switches it off with is_active false. Requires the policy.manage permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAutomationInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"automation_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"policy.manage","responses":{"200":{"description":"A standing instruction: when this kind of event happens, send the customer that message, after this long. \"When a delivery is scheduled, text the confirmation.\" \"Thirty days after a sale is fulfilled, email the care reminder.\"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/automations/run":{"post":{"operationId":"runAutomations","summary":"Queue the messages automations call for","tags":["Messaging"],"description":"Reads the events that have happened since the last run and, for each one an active automation answers (an automation answers only events after it was set up), writes the message to the customer it is about, filled in from the event's row and due after the automation's delay. Consent decides on each one: a message it does not allow is recorded as suppressed. Run it as often as wanted: each event is acted on once. It first writes the reminders that have fallen due (appointment.upcoming, delivery.site_not_ready); their messages are queued on the next run, once they reach the feed. An automation for one kind of visit (appointment_kind) answers only that kind. News of a visit (on the way, arrived, coming up, site not ready) that is over, or a crew on its way more than 6 hours ago, is not sent late: it is counted in stale_skipped.","responses":{"200":{"description":"What one run of the automations did.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRun"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/messages":{"get":{"operationId":"listMessages","summary":"List messages to customers","tags":["Messaging"],"description":"Returns messages, oldest first: everything said to customers and everything held back. On a channel with a connected email or text service the system sends service messages itself (sendDueMessages) and shows how delivery went; otherwise an outside sender takes status=queued with due=true, sends each, and records the outcome with updateMessage. A payment link's secret part is shown only to a key holding message.send. Requires the message.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only messages to this customer. Accepts the record's id or its customer number (as a number or a string of digits)."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"appointment_id","required":false,"description":"Return only messages about this visit."},{"schema":{"type":"string","enum":["queued","sending","sent","failed","suppressed"]},"in":"query","name":"status","required":false,"description":"Return only messages in this status."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"due","required":false,"description":"Send true for only messages whose send_after has passed."}],"x-required-permission":"message.view","responses":{"200":{"description":"One page of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagePage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"sendMessage","summary":"Message a customer","tags":["Messaging"],"description":"Writes a message to a customer, from a template (which sets its channel and purpose) or in your own words, to their primary address for the channel. It is queued to go out, unless their consent does not allow it: then it is recorded as suppressed with the reason, and not sent. Marketing goes only where marketing consent is granted. When the business has an email or text service connected for the channel, a service message due now is handed to it at once: the answer shows it sent, failed, or still queued for sendDueMessages. A message naming the visit it is about (appointment_id) is withdrawn while queued if the visit moves or is called off.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageInput"}}}},"responses":{"201":{"description":"One message to one customer address: waiting to be sent, sent, failed, or held back. It is both the queue the sender works from and the record of everything the tenant has said to a customer and everything it decided not to say.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/messages/{message_id}":{"patch":{"operationId":"updateMessage","summary":"Record what happened to a message","tags":["Messaging"],"description":"Records what an outside sender did with a queued message: sent, failed (with the reason) or suppressed. Marking it sent is refused if the customer withdrew consent since it was queued (mark it suppressed instead), and for a service message on a channel the business has a service connected for, which the system sends itself (sendDueMessages). Sent, failed and suppressed are final. Requires the message.send permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"message_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"x-required-permission":"message.send","responses":{"200":{"description":"One message to one customer address: waiting to be sent, sent, failed, or held back. It is both the queue the sender works from and the record of everything the tenant has said to a customer and everything it decided not to say.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/messages/send-due":{"post":{"operationId":"sendDueMessages","summary":"Send the messages that are due","tags":["Messaging"],"description":"Sends up to 100 due service messages on each channel the business has an email or text service connected for, one at a time, after settling any left in sending: a text never confirmed within an hour, or an email within a day, is marked failed with a reason saying it may have gone; an email is otherwise handed over again, which the service recognizes and never sends twice. Consent and the address are weighed again as each is taken: one they no longer allow is suppressed. Call it every minute or so, after runAutomations. Marketing messages, and every message on a channel with no service connected, are left to an outside sender (updateMessage). Requires the message.send permission.","x-required-permission":"message.send","responses":{"200":{"description":"What one call to send the due messages did.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageDispatch"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/messaging":{"get":{"operationId":"getMessagingSetup","summary":"See how email and texts are sent","tags":["Messaging"],"description":"Says, for email and for texts, whether the business has a service connected (the system then sends its service messages itself), who messages are sent as, and where the service must report.","responses":{"200":{"description":"Which channels have an email or text service connected, and who messages are sent as. Connecting one is the business's provider setup (scripts/connect-messaging.ts), not an operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagingSetup"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customer-replies":{"get":{"operationId":"listCustomerReplies","summary":"List texts from customers","tags":["Messaging"],"description":"Returns the texts customers sent the business's number, oldest first: answers to reminders, questions, and STOP, START and HELP (already acted on: STOP denies consent for that number, START gives service consent back). handled=false shows those no one has dealt with yet.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"handled","required":false,"description":"Send false for only those no one has dealt with."},{"schema":{"type":"string","x-natural-key":"customer_number"},"in":"query","name":"customer_id","required":false,"description":"Return only texts from this customer. Accepts the record's id or its customer number (as a number or a string of digits)."}],"responses":{"200":{"description":"One page of texts from customers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerReplyPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/customer-replies/{customer_reply_id}":{"patch":{"operationId":"updateCustomerReply","summary":"Mark a text from a customer handled","tags":["Messaging"],"description":"Records that someone dealt with it, and who, when the key acts for a staff member.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerReplyInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"customer_reply_id","required":true,"description":"Unique identifier (UUID v7, sortable by creation time)."}],"responses":{"200":{"description":"A text a customer sent the business's number: an answer to a reminder, a question, or STOP. Someone reads it and marks it handled. STOP, START and HELP are acted on when it arrives.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerReply"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found: The record or route in the URL does not exist for this business. `next` says how to find valid ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/audit-events":{"get":{"operationId":"listAuditEvents","summary":"See who changed what","tags":["Audit"],"description":"Returns the audit trail, oldest first: every write to a row, what changed, through which request, and who made it, named by API key and the staff member it acts for. Filter by the row for its history, by request_id to see everything one request did, by staff member, by the API key that made it (an agent's key, say), or by when. newest_first=true pages from the latest back. Changes include costs, so this needs cost.view. Secrets (key hashes, passwords, PINs, gift card codes) and what messages said are never recorded. Requires the cost.view permission.","parameters":[{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false,"description":"Maximum number of records to return."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"cursor","required":false,"description":"Pass next_cursor from the previous page to continue. Omit for the first page."},{"schema":{"type":"string","maxLength":64},"in":"query","name":"table_name","required":false,"description":"Return only changes to rows of this table."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"row_id","required":false,"description":"Return only changes to this row: its history."},{"schema":{"type":"string","pattern":"^[0-9a-fA-F-]{36}(,[0-9a-fA-F-]{36}){0,99}$"},"in":"query","name":"first_of_rows","required":false,"description":"Up to 100 row ids, comma-separated: return only each row's first change, which is what it was when it was added. To learn what rows named in other changes are."},{"schema":{"type":"string","maxLength":100},"in":"query","name":"request_id","required":false,"description":"Return only changes made by this request."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"staff_member_id","required":false,"description":"Return only changes made by keys acting for this person."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"api_key_id","required":false,"description":"Return only changes made with this API key."},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"in":"query","name":"from","required":false,"description":"Return only changes made at or after this time."},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"in":"query","name":"to","required":false,"description":"Return only changes made before this time."},{"schema":{"type":"string","enum":["true"]},"in":"query","name":"newest_first","required":false,"description":"Send true to page from the latest change back; the cursor then moves back in time."}],"x-required-permission":"cost.view","responses":{"200":{"description":"One page of the audit trail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEventPage"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden: The API key lacks a permission the action needs. Do not retry or work around it: tell the person you act for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/code-errors":{"post":{"operationId":"reportCodeError","summary":"Report a fault in Retavon","tags":["Meta"],"description":"Tells Retavon's developers about a fault you met: a response that contradicts another, a crash, an answer that makes no sense. Not for a refusal whose detail says what to do; follow that instead. The API's own failures (internal_error) are already counted. Changes nothing in the business; nobody there sees it. Repeats of one fault are counted together.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportCodeErrorInput"}}}},"responses":{"202":{"description":"The fault was received.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeErrorReceipt"}}}},"400":{"description":"bad_request: The request could not be read: malformed JSON, a wrong content type. Fix the request. validation_failed: One or more fields are missing, unknown or invalid. `errors` names each. Fix them and send again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthenticated: No valid API key was sent. Nothing you can change in the request body will help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"conflict: A record with the same unique value already exists. Use the existing record or choose another value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"invalid_reference: The request names another record by id and no such record exists. `errors` names the field and `next` says where to find valid ids. rule_violated: The request is well formed but the business does not allow it in the current state. `detail` says which rule and what to do first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"busy: The request collided with another change to the same records and was not applied. Send it again unchanged after Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"security":[{"apiKey":[]}],"tags":[{"name":"Meta","description":"Service discovery and health, and reporting a fault in the service."},{"name":"Identity","description":"Who the caller is, which business it acts for and what it may do. Start here. Also signing in, and the API keys people and agents act through."},{"name":"Locations","description":"Physical places the business operates: showrooms, warehouses, service bases."},{"name":"Staff","description":"The people who work for the business, the roles that say what they may do, and who holds which."},{"name":"Customers","description":"The people and businesses the business sells to: how to reach them and what they agreed to receive, where they take delivery, and what they owe."},{"name":"Catalog","description":"What the business sells: items and their prices, brands, categories, who supplies each item, and the options a model is built with."},{"name":"Price Lists","description":"What things cost where and for whom: a store's or region's regular prices, trade, employee and contract prices for the customers put on them, and sales; what an item would cost a customer at a store, and bringing a quote to today's prices."},{"name":"Packages","description":"Selling several things at one price: packages (a spa with its cover, steps, chemicals and delivery) and their slots, a package on a quote or order with the customer's choices, and how often each component sells."},{"name":"Sales","description":"Quotes and orders, handing goods over, returns, payments and refunds, and the one-step counter sale."},{"name":"Layaways","description":"Goods set aside for a customer and paid off by agreed dates: the schedule, what is due or late, and what cancelling keeps. Payments are ordinary payments on the order; the goods go home once it is paid in full."},{"name":"Leads","description":"Someone asking about buying, from the first question to the agreed order: taking a lead down, who follows it up and when, what happened, losing it with why, and the pipeline by source, salesperson and store. A lead is won by an order linked to it."},{"name":"Trade-Ins","description":"A customer's old unit taken in toward a sale: what they are allowed for it, receiving it into stock (which pays the allowance toward the order), giving it back, and the traded units out back to sell, write down or haul away."},{"name":"Counter","description":"Cash at the counter: registers and their drawers from the float to the count, cash paid in and out for reasons other than a sale, drops, and each location's safe and its trips to the bank."},{"name":"Receipts","description":"Receipts for what happened at the counter: issuing one for an order's sale, deposit, payment, return or refund, printing it (again, or as a gift receipt) and emailing it. Payments on account and gift card sales issue their own."},{"name":"Card Payments","description":"Cards taken through the business's card processor: on a reader at the counter or on delivery day, by a link the customer pays from home, or from a card kept on file for a care plan; a counter sale paid on the reader, written only once the card is approved; given back; and paid out to the bank, less the processor's fees. Each charge records its payment on the order."},{"name":"Gift Cards","description":"Gift cards: selling and reloading them, what a card holds, voiding a sale the same day, blocking and replacing a lost card, promotional cards, and writing off what will never be spent. A card is spent with takePayment or recordCounterSale, sending its code."},{"name":"Service","description":"After the sale: customers' equipment and its warranties, and work orders for repairs and service."},{"name":"Warranty","description":"Getting paid for warranty work: each manufacturer's claim terms, claims from draft to paid, the failed parts they want back, and what each owes."},{"name":"Subscriptions","description":"Care plans and auto-ships: what a customer gets again and again on a schedule, each cycle an ordinary order."},{"name":"Field Work","description":"The schedule: deliveries, service calls and consultations at customers' sites, who is going, each person's day in route order, and the crew's day (on the way, arrived, photos)."},{"name":"Field Service","description":"A technician's day on a service job: the day's stops and parts to bring, kinds of job with their steps and parts, time by step, parts used from the van, photos, the job's checklist, the customer's signature, and finishing a visit so labor and parts are charged to the customer or claimed from the manufacturer. Every write takes the device's time and a key, so an offline device can upload late."},{"name":"Deliveries","description":"Getting a delivery to the customer: whether the site is ready (site checks and outside parties such as a crane or the customer's electrician), the customer's sign-off or a failed attempt, a redelivery, and the evidence for a dispute."},{"name":"Files","description":"Photos, signatures and documents: upload once, then attach."},{"name":"Commission","description":"How salespeople are paid, what they have earned, and sales targets with how far along each is."},{"name":"Financing","description":"Selling on a consumer lender's promotions (the customer borrows; loans on the business's own stock are Floor Plans): the lenders and plans the business offers, monthly payment estimates, each customer's application and the lender's answer, and the lender's payouts and dealer fees."},{"name":"Receivables","description":"Who owes the business money, how much, and how late; what customers hold on deposit; payments on account; and the statements customers are sent."},{"name":"Water Care","description":"The water_care module: customers' hot tubs and pools, water tests and what to add. Not found for a business without the module."},{"name":"Inventory","description":"Stock: what is on hand where and what it cost, every movement, individual serialized units, and transfers between locations."},{"name":"Stock Counts","description":"Counting what is on the shelf at a location, by quantity and, for spas and other serialized units, by scanning serial numbers; posting a count corrects the stock to it."},{"name":"Purchasing","description":"Buying stock: the vendors the business buys from, purchase orders, receiving goods, and sending them back."},{"name":"Floor Plans","description":"Units bought on a floor-plan line of credit: the lenders and their terms, what each unit owes, curtailments and payoffs due, interest, payments to the lenders, and the unit-by-unit audit a lender checks."},{"name":"Tax","description":"Sales tax: the classes items are taxed by, the jurisdictions that charge it and their rates, and the areas where each applies."},{"name":"Settings","description":"How the business is set up: the ways customers pay, the policies it has chosen, the fields it has added to records, the checklists its people answer on a job, and the card readers at its counters and on its trucks."},{"name":"Messaging","description":"What the business says to customers, by email and text: templates, automations that send them when something happens, and every message sent or held back for want of consent."},{"name":"Integrations","description":"How outside systems keep up: the event feed and its readers, links between records here and records outside, and journal batches for the accounting system."},{"name":"Audit","description":"Who changed what, when, and through which request: every write to every row."},{"name":"Reports","description":"Figures across the business, read live from the books and the records behind them: sales by any grouping against another period, a store's day closed, and the books as statements."},{"name":"Books","description":"The chart of accounts, the journal, and the check that the books are consistent."}]}