[{"data":1,"prerenderedAt":930},["ShallowReactive",2],{"docs-nav-docs_en":3,"docs-search-docs_en":76,"doc-docs_en-\u002Fdocs\u002Fapi\u002Fconventions":461,"surround-docs_en-\u002Fdocs\u002Fapi\u002Fconventions":926},[4],{"title":5,"path":6,"stem":7,"children":8,"page":42},"Docs","\u002Fdocs","docs",[9,13,17,21,43,47,68,72],{"title":10,"path":11,"stem":12},"Introduction","\u002Fdocs\u002Fintroduction","docs\u002F1.introduction",{"title":14,"path":15,"stem":16},"Quickstart","\u002Fdocs\u002Fquickstart","docs\u002F2.quickstart",{"title":18,"path":19,"stem":20},"How it works","\u002Fdocs\u002Fhow-it-works","docs\u002F3.how-it-works",{"title":22,"path":23,"stem":24,"children":25,"page":42},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F4.concepts",[26,30,34,38],{"title":27,"path":28,"stem":29},"Workspaces and resources","\u002Fdocs\u002Fconcepts\u002Fworkspaces-and-resources","docs\u002F4.concepts\u002F1.workspaces-and-resources",{"title":31,"path":32,"stem":33},"Services and availability","\u002Fdocs\u002Fconcepts\u002Fservices-and-availability","docs\u002F4.concepts\u002F2.services-and-availability",{"title":35,"path":36,"stem":37},"Bookings and holds","\u002Fdocs\u002Fconcepts\u002Fbookings-and-holds","docs\u002F4.concepts\u002F3.bookings-and-holds",{"title":39,"path":40,"stem":41},"Automations","\u002Fdocs\u002Fconcepts\u002Fautomations","docs\u002F4.concepts\u002F4.automations",false,{"title":44,"path":45,"stem":46},"WhatsApp and AI","\u002Fdocs\u002Fwhatsapp-and-ai","docs\u002F5.whatsapp-and-ai",{"title":48,"path":49,"stem":50,"children":51,"page":42},"API","\u002Fdocs\u002Fapi","docs\u002F6.api",[52,56,60,64],{"title":53,"path":54,"stem":55},"Authentication","\u002Fdocs\u002Fapi\u002Fauthentication","docs\u002F6.api\u002F1.authentication",{"title":57,"path":58,"stem":59},"Conventions","\u002Fdocs\u002Fapi\u002Fconventions","docs\u002F6.api\u002F2.conventions",{"title":61,"path":62,"stem":63},"Endpoints","\u002Fdocs\u002Fapi\u002Fendpoints","docs\u002F6.api\u002F3.endpoints",{"title":65,"path":66,"stem":67},"Webhooks","\u002Fdocs\u002Fapi\u002Fwebhooks","docs\u002F6.api\u002F4.webhooks",{"title":69,"path":70,"stem":71},"MCP and AI agents","\u002Fdocs\u002Fmcp","docs\u002F7.mcp",{"title":73,"path":74,"stem":75},"Recipes by business","\u002Fdocs\u002Frecipes","docs\u002F8.recipes",[77,81,87,92,97,102,105,110,115,120,125,130,135,140,145,148,153,158,163,168,171,176,181,186,191,194,199,204,209,214,219,222,227,232,237,242,247,250,255,260,265,270,275,278,283,288,293,298,303,308,311,316,321,326,329,334,339,344,349,354,357,362,367,372,377,382,387,390,395,400,405,410,413,418,423,428,433,436,441,446,451,456],{"id":11,"title":10,"titles":78,"content":79,"level":80},[],"What Wagend is, who it is for and the building blocks you will use. Wagend is a scheduling engine with WhatsApp + AI as its main channel. Your customers book by chatting; the system offers only slots that really exist, holds them, confirms them and sends reminders. Businesses manage everything from a dashboard, and developers integrate through a REST API, webhooks and an MCP server. Wagend is in development. This documentation describes the target contract (API v1 draft). Endpoints may still change before the pilot.",1,{"id":82,"title":83,"titles":84,"content":85,"level":86},"\u002Fdocs\u002Fintroduction#who-it-is-for","Who it is for",[10],"Any business that sells someone's or something's time: BusinessWhat gets bookedBarbershopA barber for 30–45 minutesAestheticsA professional and a machine and a room, at the same timeClinicA doctor and an officeRestaurantCovers in a dining room for a party sizeDeliveriesCapacity in a 2-hour window for a zoneClasses, courts, roomsA seat in a class or a court by the hour",2,{"id":88,"title":89,"titles":90,"content":91,"level":86},"\u002Fdocs\u002Fintroduction#building-blocks","Building blocks",[10],"Organization → your account. It holds one or more workspaces (a business or location).Resource → what gets consumed: a barber, a table, a laser machine, a room, a delivery zone.Service → what the customer books. It declares its requirements: which resources it needs, and how many, at the same time.Schedule → when each resource is available.Booking → goes through a hold (10 minutes) before being confirmed.Automation → reminders, confirmations and staff alerts triggered by booking events. Read Workspaces and resources for the full model.",{"id":93,"title":94,"titles":95,"content":96,"level":86},"\u002Fdocs\u002Fintroduction#channels","Channels",[10],"All channels use the same engine, so a slot booked on WhatsApp disappears instantly from the booking page and the API. WhatsApp with an AI assistant (text and voice notes).Public booking page and embeddable widget.Dashboard for the team (today's agenda, inbox, calendar, settings).REST API, webhooks and MCP for developers and AI agents.",{"id":98,"title":99,"titles":100,"content":101,"level":86},"\u002Fdocs\u002Fintroduction#next-steps","Next steps",[10],"Quickstart: make your first booking through the API in 5 minutes.How it works: the architecture and the life of a message.MCP: connect an AI agent to Wagend.",{"id":15,"title":14,"titles":103,"content":104,"level":80},[],"Create your first booking through the API in five minutes using a sandbox key. This guide uses a test key (wg_test_...). Test keys work against a sandbox copy of your workspace: no WhatsApp messages are sent and no real payments are made. Base URL: https:\u002F\u002Fapi.wagend.app\u002Fv1. Every request needs Authorization: Bearer \u003Ckey>.",{"id":106,"title":107,"titles":108,"content":109,"level":86},"\u002Fdocs\u002Fquickstart#_1-get-a-test-key","1. Get a test key",[14],"In the dashboard, go to Developers → API keys → New key, choose Test, and select the scopes slots:read, bookings:write and config:read. The key is shown only once. export WAGEND_KEY=\"wg_test_xxxxxxxx_xxxxxxxxxxxxxxxxxxxx\"",{"id":111,"title":112,"titles":113,"content":114,"level":86},"\u002Fdocs\u002Fquickstart#_2-check-who-you-are","2. Check who you are",[14],"curl https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fme -H \"Authorization: Bearer $WAGEND_KEY\" {\n  \"workspace\": { \"id\": \"ws_9f2c\", \"name\": \"Downtown Barbershop\", \"timezone\": \"America\u002FSao_Paulo\", \"locale\": \"pt\", \"currency\": \"BRL\" },\n  \"scopes\": [\"slots:read\", \"bookings:write\", \"config:read\"],\n  \"mode\": \"test\"\n} The workspace always comes from the key. You never send a workspace id.",{"id":116,"title":117,"titles":118,"content":119,"level":86},"\u002Fdocs\u002Fquickstart#_3-list-services","3. List services",[14],"curl https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fservices -H \"Authorization: Bearer $WAGEND_KEY\" {\n  \"data\": [\n    { \"id\": \"svc_corte\", \"name\": \"Haircut\", \"duration_min\": 30, \"price_cents\": 5000, \"currency\": \"BRL\",\n      \"requirements\": [{ \"resource_group_id\": \"grp_barbers\", \"units\": 1 }] }\n  ]\n}",{"id":121,"title":122,"titles":123,"content":124,"level":86},"\u002Fdocs\u002Fquickstart#_4-find-available-slots","4. Find available slots",[14],"curl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fslots?service_id=svc_corte&from=2026-10-15T08:00:00-03:00&to=2026-10-15T13:00:00-03:00\" \\\n  -H \"Authorization: Bearer $WAGEND_KEY\" {\n  \"data\": [\n    { \"start\": \"2026-10-15T09:00:00-03:00\", \"end\": \"2026-10-15T09:30:00-03:00\", \"resource_ids\": [\"res_juan\"] },\n    { \"start\": \"2026-10-15T09:45:00-03:00\", \"end\": \"2026-10-15T10:15:00-03:00\", \"resource_ids\": [\"res_juan\"] }\n  ],\n  \"unavailable_reason\": null\n}",{"id":126,"title":127,"titles":128,"content":129,"level":86},"\u002Fdocs\u002Fquickstart#_5-hold-the-slot","5. Hold the slot",[14],"A hold reserves the slot for 10 minutes while you collect the customer's details. Always send an Idempotency-Key so retries are safe. curl -X POST https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fholds \\\n  -H \"Authorization: Bearer $WAGEND_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"service_id\": \"svc_corte\", \"start\": \"2026-10-15T09:45:00-03:00\", \"resource_ids\": [\"res_juan\"] }' { \"id\": \"bkg_7Qx1\", \"status\": \"held\", \"expires_at\": \"2026-10-14T15:30:00-03:00\", \"start\": \"2026-10-15T09:45:00-03:00\" }",{"id":131,"title":132,"titles":133,"content":134,"level":86},"\u002Fdocs\u002Fquickstart#_6-confirm","6. Confirm",[14],"curl -X POST https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fholds\u002Fbkg_7Qx1\u002Fconfirm \\\n  -H \"Authorization: Bearer $WAGEND_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"customer\": { \"name\": \"Carlos\", \"phone\": \"+5521999990000\", \"locale\": \"pt\" } }' { \"id\": \"bkg_7Qx1\", \"status\": \"confirmed\", \"start\": \"2026-10-15T09:45:00-03:00\", \"source\": \"api\" } If the service requires a Pix deposit, the status is pending_payment and the response includes the Pix code. It becomes confirmed when the payment arrives.",{"id":136,"title":137,"titles":138,"content":139,"level":86},"\u002Fdocs\u002Fquickstart#same-flow-in-javascript","Same flow in JavaScript",[14],"const api = (path: string, init: RequestInit = {}) =>\n  fetch(`https:\u002F\u002Fapi.wagend.app\u002Fv1${path}`, {\n    ...init,\n    headers: { Authorization: `Bearer ${process.env.WAGEND_KEY}`, 'Content-Type': 'application\u002Fjson', ...init.headers },\n  }).then((r) => r.json())\n\nconst { data: slots } = await api(`\u002Fslots?service_id=svc_corte&from=${from}&to=${to}`)\nconst hold = await api('\u002Fholds', {\n  method: 'POST',\n  headers: { 'Idempotency-Key': crypto.randomUUID() },\n  body: JSON.stringify({ service_id: 'svc_corte', start: slots[0].start }),\n})\nconst booking = await api(`\u002Fholds\u002F${hold.id}\u002Fconfirm`, {\n  method: 'POST',\n  headers: { 'Idempotency-Key': crypto.randomUUID() },\n  body: JSON.stringify({ customer: { name: 'Carlos', phone: '+5521999990000' } }),\n})",{"id":141,"title":142,"titles":143,"content":144,"level":86},"\u002Fdocs\u002Fquickstart#next","Next",[14],"React to bookings with webhooks.See every endpoint in the reference. html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"id":19,"title":18,"titles":146,"content":147,"level":80},[],"Architecture, principles and the life of a WhatsApp message inside Wagend.",{"id":149,"title":150,"titles":151,"content":152,"level":86},"\u002Fdocs\u002Fhow-it-works#principles","Principles",[18],"Deterministic core, AI as interpreter. The AI understands the customer and calls tools. The engine computes availability, holds, confirms and charges. The AI never invents a time or a price.The database enforces the rules. Double booking is impossible because PostgreSQL rejects overlapping allocations, not because the app \"checks first\".API-first. The dashboard, the booking page, the WhatsApp bot and MCP all use the same services. If something can't be done through the API, it's missing from the API.The credential defines the tenant. A key or session always belongs to one workspace.",{"id":154,"title":155,"titles":156,"content":157,"level":86},"\u002Fdocs\u002Fhow-it-works#architecture","Architecture",[18],"Channels:   WhatsApp · Booking page · Dashboard · REST \u002F MCP\n                 │\nGateway:    credential → workspace · scopes · rate limits · signed webhooks\n                 │\nCore:       conversations + AI │ scheduling engine │ automations │ payments │ knowledge\n                 │\nData:       PostgreSQL (row-level security, exclusion constraints, jobs) · Redis\nExternal:   AI models (OpenRouter) · speech-to-text · WhatsApp provider · Pix",{"id":159,"title":160,"titles":161,"content":162,"level":86},"\u002Fdocs\u002Fhow-it-works#the-life-of-a-message","The life of a message",[18],"Arrives. The WhatsApp provider calls our webhook. We verify the HMAC signature, drop duplicates by message id and store it. We answer in under 200 ms.Understood. Voice notes are transcribed. We identify the customer and the conversation. If a person took over, the bot stays silent.The AI asks. The model gets the business context, the local date and time, a structured conversation state and the last messages. It can only call tools such as find_slots, hold_slot or confirm_hold.The engine decides. Each tool call is validated against a strict schema and business rules, then executed. Errors come back as structured data (for example slot_taken with alternatives) so the AI asks again.Exact reply. Dates, times, services, prices and addresses are rendered from templates filled with the real result.Events. booking.confirmed and friends trigger automations (reminders) and your webhooks.",{"id":164,"title":165,"titles":166,"content":167,"level":86},"\u002Fdocs\u002Fhow-it-works#where-to-go-next","Where to go next",[18],"Bookings and holds explains the guarantees.WhatsApp and AI explains what the AI can and cannot do.",{"id":28,"title":27,"titles":169,"content":170,"level":80},[],"Organizations, workspaces, roles, resources, capacity modes and resource groups.",{"id":172,"title":173,"titles":174,"content":175,"level":86},"\u002Fdocs\u002Fconcepts\u002Fworkspaces-and-resources#organization-and-workspaces","Organization and workspaces",[27],"An organization is the account that pays. It contains one or more workspaces. A workspace is a business or a location with its own: time zone, language and currency,WhatsApp number(s),bot, services, resources and team. A chain of three barbershops is one organization with three workspaces.",{"id":177,"title":178,"titles":179,"content":180,"level":86},"\u002Fdocs\u002Fconcepts\u002Fworkspaces-and-resources#roles","Roles",[27],"RoleCanownerEverything, across all workspaces of the organizationmanagerEverything inside one workspacestaffSee and manage only their own resources (for example, a barber sees only their agenda)viewerRead-only Staff can also act from WhatsApp after linking their number with a one-time code. Admin actions from WhatsApp always ask for a YES\u002FNO confirmation.",{"id":182,"title":183,"titles":184,"content":185,"level":86},"\u002Fdocs\u002Fconcepts\u002Fworkspaces-and-resources#resources","Resources",[27],"A resource is anything that gets consumed by a booking. kindExamplesstaffBarber, doctor, beautician, instructorspaceRoom, office, booth, dining roomequipmentLaser machine, chair, courttableRestaurant table (table mode)vehicleVan, delivery crewzoneDelivery area by postal code Each resource has a capacity mode: exclusive — serves one booking at a time (a barber, a room, a machine).pooled — has a capacity in units (40 covers per slot, 12 seats in a class, 10 deliveries per window).",{"id":187,"title":188,"titles":189,"content":190,"level":86},"\u002Fdocs\u002Fconcepts\u002Fworkspaces-and-resources#resource-groups","Resource groups",[27],"A resource group is an interchangeable pool (\"any barber\", \"laser machines\"). Services require groups, not individual resources, so the engine can pick one. The assignment strategy decides which: assignmentBehaviorcustomer_choiceThe customer picks (for example \"with Juan\"); falls back to another strategy if they don't careround_robinRotates between membersleast_busyPicks the member with the fewest bookings that dayfixedAlways the same member (single machine, single room) {\n  \"name\": \"Barbers\",\n  \"assignment\": \"customer_choice\",\n  \"member_ids\": [\"res_juan\", \"res_pedro\", \"res_leo\"]\n} html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":32,"title":31,"titles":192,"content":193,"level":80},[],"Services, multi-resource requirements, schedules and how slots are computed.",{"id":195,"title":196,"titles":197,"content":198,"level":86},"\u002Fdocs\u002Fconcepts\u002Fservices-and-availability#services","Services",[31],"A service is what the customer books. FieldMeaningduration_minLength of the servicestep_minGrid for start times (every 15, 30 minutes…)buffer_before_min \u002F buffer_after_minCleanup or preparation time blocked around the bookingprice_cents, currencyPrice shown by the bot (always from the database)deposit_centsPix deposit required to confirmlead_time_minMinimum notice (for example 60 minutes)max_advance_daysHow far ahead customers can bookwindow_modeThe slot is a fixed window (deliveries: 14:00–16:00)fulfillment_modescheduled (default, with a time slot) or queue (dispatch by queue, no fixed time)intake_formJSON Schema of extra data to collect (insurance, address…)requirementsWhich resources the service needs at the same time",{"id":200,"title":201,"titles":202,"content":203,"level":86},"\u002Fdocs\u002Fconcepts\u002Fservices-and-availability#window-or-dispatch-two-fulfillment-modes","Window or dispatch: two fulfillment modes",[31],"Every service defines how the booking is fulfilled (fulfillment_mode): scheduled (the default): the booking occupies a known time range — a fixed slot or, with window_mode, a window (deliveries from 14:00 to 16:00, with capacity per zone).queue (dispatch, for water, gas or on-demand delivery): the booking has no fixed time — it joins a courier's backlog queue. The engine assigns it to the courier with the fewest active tasks, and each courier has a cap on active tasks at a time, enforced in the database. The queue uses the same states as a booking: pending → accepted → started → completed (or failed). So the same delivery business can offer scheduled windows, immediate dispatch, or both.",{"id":205,"title":206,"titles":207,"content":208,"level":86},"\u002Fdocs\u002Fconcepts\u002Fservices-and-availability#requirements","Requirements",[31],"A requirement says: from this group, I need this many units. A service can have several; a slot exists only if all of them are free. {\n  \"name\": \"Laser hair removal\",\n  \"duration_min\": 45,\n  \"buffer_after_min\": 10,\n  \"requirements\": [\n    { \"resource_group_id\": \"grp_professionals\", \"units\": 1 },\n    { \"resource_group_id\": \"grp_lasers\", \"units\": 1 },\n    { \"resource_group_id\": \"grp_rooms\", \"units\": 1 }\n  ]\n} With two professionals but only one laser, Wagend will never offer two overlapping laser sessions. Advanced options: units: \"party_size\" — consume as many units as people (restaurants, classes).offset_min \u002F duration_min on a requirement — use a resource for part of the service only (for example the wash basin during the last 15 minutes of a hair color).",{"id":210,"title":211,"titles":212,"content":213,"level":86},"\u002Fdocs\u002Fconcepts\u002Fservices-and-availability#schedules","Schedules",[31],"Each resource has a schedule: weekly rules in the resource's time zone plus overrides for specific dates (closed, or special hours). {\n  \"tz\": \"America\u002FSao_Paulo\",\n  \"weekly\": { \"tue\": [[\"09:00\", \"19:00\"]], \"sat\": [[\"08:00\", \"12:00\"], [\"13:00\", \"16:00\"]] },\n  \"overrides\": [{ \"date\": \"2026-12-24\", \"ranges\": [[\"09:00\", \"13:00\"]] }, { \"date\": \"2026-12-25\", \"closed\": true }]\n}",{"id":215,"title":216,"titles":217,"content":218,"level":86},"\u002Fdocs\u002Fconcepts\u002Fservices-and-availability#how-slots-are-computed","How slots are computed",[31],"Expand the schedules for the requested range, in each resource's time zone.Remove active bookings and holds (plus buffers) and manual blocks.Cut into candidate slots using the service duration and step.Apply lead time, max advance, cutoff rules and party size.Cross all requirements, trying group members according to the assignment strategy.Return a short, ordered list. When nothing is available, the response includes unavailable_reason (closed, no_staff, no_equipment, no_space, lead_time, max_advance, cutoff, full) so the bot can explain why. html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":36,"title":35,"titles":220,"content":221,"level":80},[],"Booking states, the 10-minute hold, Pix deposits and the no-double-booking guarantee.",{"id":223,"title":224,"titles":225,"content":226,"level":86},"\u002Fdocs\u002Fconcepts\u002Fbookings-and-holds#why-a-hold","Why a hold",[35],"A WhatsApp conversation can take minutes: the customer picks a time, then you ask their name, then maybe a deposit. Meanwhile other customers, the booking page and the API are competing for the same slot. A hold reserves the slot for 10 minutes (15 when a Pix deposit is required) and expires on its own.",{"id":228,"title":229,"titles":230,"content":231,"level":86},"\u002Fdocs\u002Fconcepts\u002Fbookings-and-holds#states","States",[35],"StatusMeaningOccupies the slotheldTemporary reservationYespending_paymentWaiting for the Pix depositYesconfirmedBookedYeschecked_inThe customer arrivedYescompletedService doneNocancelledCancelled (or replaced by a reschedule)Nono_showMarked absent by a personNoexpiredHold not confirmed in timeNo held ──confirm──► confirmed ──check-in──► checked_in ──► completed\n │  └─(deposit)─► pending_payment ──paid──► confirmed\n └─expire──► expired          confirmed ──cancel──► cancelled",{"id":233,"title":234,"titles":235,"content":236,"level":86},"\u002Fdocs\u002Fconcepts\u002Fbookings-and-holds#guarantees","Guarantees",[35],"Every resource a booking occupies is stored as an allocation with a time range.For exclusive resources, PostgreSQL rejects overlapping allocations with an exclusion constraint.For pooled resources, capacity counters can never exceed the limit (checked atomically).A multi-resource booking is written in a single transaction: if any resource clashes, nothing is booked.POST \u002Fholds and POST \u002Fholds\u002F{id}\u002Fconfirm require an Idempotency-Key: retrying a request never creates a second booking. If a slot was taken while the customer was deciding, you get 409 with code: \"slot_taken\" and a list of alternatives.",{"id":238,"title":239,"titles":240,"content":241,"level":86},"\u002Fdocs\u002Fconcepts\u002Fbookings-and-holds#reschedule-and-cancel","Reschedule and cancel",[35],"Reschedule creates a new booking and cancels the old one (linked by rescheduled_to). Reminders move automatically.Cancel frees the slot and cancels pending reminders.No-show is never automatic: the system suggests it, a person confirms it.",{"id":243,"title":244,"titles":245,"content":246,"level":86},"\u002Fdocs\u002Fconcepts\u002Fbookings-and-holds#deposits","Deposits",[35],"If the service has deposit_cents, confirming a hold returns pending_payment with a Pix code. When the payment provider notifies us, the booking becomes confirmed and payment.paid is emitted. A payment that arrives after the hold expired is flagged for refund.",{"id":40,"title":39,"titles":248,"content":249,"level":80},[],"Reminders, confirmations and alerts as \"when this happens, do that\" rules. Automations are rules made of a trigger, optional conditions and an action. Each business template ships with sensible defaults you can edit.",{"id":251,"title":252,"titles":253,"content":254,"level":86},"\u002Fdocs\u002Fconcepts\u002Fautomations#triggers","Triggers",[39],"TriggerExamplebooking.confirmedSend the booking summarybooking.starts_in(Δ)24 h before: ask for YES\u002FNO confirmationbooking.ended(+Δ)1 h after: suggest checking attendancebooking.cancelled, booking.rescheduledNotify staffhold.expiredFollow up with the customerpayment.paidConfirm and thankdaily_at(hh:mm)Send the day's agenda to staff",{"id":256,"title":257,"titles":258,"content":259,"level":86},"\u002Fdocs\u002Fconcepts\u002Fautomations#conditions","Conditions",[39],"Service, resource, party size above N, channel, customer tag, number of past no-shows.",{"id":261,"title":262,"titles":263,"content":264,"level":86},"\u002Fdocs\u002Fconcepts\u002Fautomations#actions","Actions",[39],"Send a message or template to the customer, notify staff, request a YES\u002FNO confirmation, change the booking status, create an internal task, call a webhook.",{"id":266,"title":267,"titles":268,"content":269,"level":86},"\u002Fdocs\u002Fconcepts\u002Fautomations#defaults-by-business","Defaults by business",[39],"TemplateAutomationsBarbershop, aesthetics, clinicReminder 24 h before with YES\u002FNO · staff heads-up 2 h before · attendance check 1 h after · daily agenda 7:30RestaurantReminder the day before · confirmation required for parties above 6 · daily agendaDeliveries\"On the way\" 1 h before · delivery received confirmation",{"id":271,"title":272,"titles":273,"content":274,"level":86},"\u002Fdocs\u002Fconcepts\u002Fautomations#execution-guarantees","Execution guarantees",[39],"Jobs are created in the same transaction as the booking event, so they are never lost.Before running, a job re-checks the booking. If it was rescheduled or cancelled, the old job is discarded.Each job runs once (idempotency key) and retries with backoff; after 5 failures it shows up in the dashboard.Quiet hours: nothing is sent to customers between 21:00 and 08:00 in their time zone.YES\u002FNO answers are parsed without AI (sim, sí, ok, 👍, não…), which is instant and free.",{"id":45,"title":44,"titles":276,"content":277,"level":80},[],"Connecting a number, what the assistant can and cannot do, staff actions and human handoff.",{"id":279,"title":280,"titles":281,"content":282,"level":86},"\u002Fdocs\u002Fwhatsapp-and-ai#connecting-a-number","Connecting a number",[44],"In the dashboard, go to Channels → WhatsApp → Connect and scan the QR code with the business phone, just like WhatsApp Web. A workspace can connect several numbers (one per location, for example). Use a number dedicated to the business. For the green badge or bulk messaging, an official WhatsApp Business Platform connection will be offered as an option.",{"id":284,"title":285,"titles":286,"content":287,"level":86},"\u002Fdocs\u002Fwhatsapp-and-ai#customers-vs-staff","Customers vs staff",[44],"The role is decided in code, never by what someone says in the chat: A number linked to a team member (verified with a one-time code) gets staff tools.Everyone else gets customer tools. Customer toolsStaff tools (extra)list_services, find_slots, hold_slot, confirm_hold, my_bookings, cancel_my_booking, reschedule_my_booking, get_business_info, search_knowledge, handoff_to_humanlist_today, block_time, mark_no_show, update_catalog_item — always with a YES\u002FNO confirmation",{"id":289,"title":290,"titles":291,"content":292,"level":86},"\u002Fdocs\u002Fwhatsapp-and-ai#what-the-ai-can-and-cannot-do","What the AI can and cannot do",[44],"CanCannotUnderstand text, voice notes, typos and changes of mindInvent times, prices or addressesFind slots and offer 2–3 optionsConfirm a booking without the engineHold a slot and ask for missing detailsUse admin tools with a customerAnswer questions from the business knowledge baseFollow instructions hidden in messages or filesHand over to a personSee another business's data Workspace ids, customer ids and prices are injected by the system; the model never supplies them.",{"id":294,"title":295,"titles":296,"content":297,"level":86},"\u002Fdocs\u002Fwhatsapp-and-ai#human-handoff","Human handoff",[44],"The team sees every conversation live in the Inbox. Taking over pauses the bot for that conversation; it resumes automatically after 15 minutes without activity from the operator. The assistant also hands over on request (\"I want to talk to a person\").",{"id":299,"title":300,"titles":301,"content":302,"level":86},"\u002Fdocs\u002Fwhatsapp-and-ai#voice-notes-and-languages","Voice notes and languages",[44],"Voice notes are transcribed before the AI reads them, and the transcript is shown in the inbox. The assistant replies in the customer's language (Portuguese, Spanish or English).",{"id":304,"title":305,"titles":306,"content":307,"level":86},"\u002Fdocs\u002Fwhatsapp-and-ai#models","Models",[44],"The AI layer runs through OpenRouter with a dedicated key and spending limit per workspace. The default is a fast, low-cost model; a stronger model is used only for turns that fail validation. Providers are pinned to ones that do not retain data.",{"id":54,"title":53,"titles":309,"content":310,"level":80},[],"API keys, scopes, test and live modes.",{"id":312,"title":313,"titles":314,"content":315,"level":86},"\u002Fdocs\u002Fapi\u002Fauthentication#api-keys","API keys",[53],"Send your key as a bearer token: GET \u002Fv1\u002Fme HTTP\u002F1.1\nHost: api.wagend.app\nAuthorization: Bearer wg_live_3fa9c2_Lr8t... PrefixModewg_live_Production data, real messages and paymentswg_test_Sandbox: isolated data, no messages sent, simulated payments Keys belong to one workspace. The workspace is always taken from the key: there is no workspace id in paths or bodies. Keys are stored hashed; the full key is shown only once at creation.",{"id":317,"title":318,"titles":319,"content":320,"level":86},"\u002Fdocs\u002Fapi\u002Fauthentication#scopes","Scopes",[53],"ScopeAllowsslots:readGET \u002Fslotsbookings:readList and read bookingsbookings:writeHolds, confirm, cancel, reschedule, check-in, no-showcustomers:readRead customersmessages:readRead conversations and messagesmessages:writeSend messages, hand overconfig:read \u002F config:writeServices, resources, groups, schedules, automations, knowledgewebhooks:manageWebhook endpoints A request without the needed scope returns 403 with code: \"insufficient_scope\".",{"id":322,"title":323,"titles":324,"content":325,"level":86},"\u002Fdocs\u002Fapi\u002Fauthentication#rotation","Rotation",[53],"Create a new key, deploy it, then revoke the old one in Developers → API keys. Revoked keys return 401 immediately. html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":58,"title":57,"titles":327,"content":328,"level":80},[],"Formats, idempotency, pagination, errors and rate limits.",{"id":330,"title":331,"titles":332,"content":333,"level":86},"\u002Fdocs\u002Fapi\u002Fconventions#basics","Basics",[57],"Base URL https:\u002F\u002Fapi.wagend.app\u002Fv1. Breaking changes ship as a new version.JSON in and out (Content-Type: application\u002Fjson).Timestamps in ISO 8601 with offset (2026-10-15T09:45:00-03:00). Stored in UTC.Money in integer cents plus currency (BRL, ARS, PYG, USD).IDs are opaque strings with a prefix (bkg_, svc_, res_, grp_, cus_).",{"id":335,"title":336,"titles":337,"content":338,"level":86},"\u002Fdocs\u002Fapi\u002Fconventions#idempotency","Idempotency",[57],"POST \u002Fholds, POST \u002Fholds\u002F{id}\u002Fconfirm, POST \u002Fbookings and POST \u002Fbookings\u002F{id}\u002Freschedule require an Idempotency-Key header (for example a UUID). Repeating a request with the same key within 24 hours returns the original response. Reusing a key with a different body returns 422.",{"id":340,"title":341,"titles":342,"content":343,"level":86},"\u002Fdocs\u002Fapi\u002Fconventions#pagination","Pagination",[57],"List endpoints use cursors: curl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50\" -H \"Authorization: Bearer $WAGEND_KEY\"\n# → { \"data\": [...], \"next_cursor\": \"eyJpZCI6...\" }\ncurl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50&cursor=eyJpZCI6...\" -H \"Authorization: Bearer $WAGEND_KEY\"",{"id":345,"title":346,"titles":347,"content":348,"level":86},"\u002Fdocs\u002Fapi\u002Fconventions#errors","Errors",[57],"Errors follow RFC 9457 (application\u002Fproblem+json): {\n  \"type\": \"https:\u002F\u002Fwagend.app\u002Fdocs\u002Fapi\u002Fconventions#errors\",\n  \"title\": \"Slot no longer available\",\n  \"status\": 409,\n  \"code\": \"slot_taken\",\n  \"alternatives\": [{ \"start\": \"2026-10-15T10:15:00-03:00\", \"end\": \"2026-10-15T10:45:00-03:00\" }]\n} codeStatusWheninvalid_request400 \u002F 422Validation failedunauthorized401Missing, invalid or revoked keyinsufficient_scope403Key lacks the scopenot_found404Unknown id (or belongs to another workspace)slot_taken409Someone else got the slotinvalid_transition409For example confirming a cancelled bookinghold_expired410The hold expired before confirmidempotency_mismatch422Same key, different bodyrate_limited429Too many requests",{"id":350,"title":351,"titles":352,"content":353,"level":86},"\u002Fdocs\u002Fapi\u002Fconventions#rate-limits","Rate limits",[57],"Limits apply per key and per workspace. Every response includes RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. On 429, wait Retry-After seconds. html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":62,"title":61,"titles":355,"content":356,"level":80},[],"Every v1 endpoint at a glance, with the most common request and response shapes. The machine-readable contract lives in the repository at packages\u002Fopenapi\u002Fopenapi.yaml (OpenAPI 3.1).",{"id":358,"title":359,"titles":360,"content":361,"level":86},"\u002Fdocs\u002Fapi\u002Fendpoints#scheduling","Scheduling",[61],"MethodPathScopeDescriptionGET\u002Fslotsslots:readAvailable slots for a servicePOST\u002Fholdsbookings:writeCreate a 10-minute holdPOST\u002Fholds\u002F{id}\u002Fconfirmbookings:writeConfirm (or pending_payment if a deposit is required)DELETE\u002Fholds\u002F{id}bookings:writeRelease a holdGET\u002Fbookingsbookings:readList (filters: from, to, status, resource_id, customer_id)POST\u002Fbookingsbookings:writeHold + confirm in one callGET \u002F PATCH\u002Fbookings\u002F{id}bookings:read \u002F writeRead, update notes or intake dataPOST\u002Fbookings\u002F{id}\u002Fcancelbookings:writeCancelPOST\u002Fbookings\u002F{id}\u002Freschedulebookings:writeMove to a new startPOST\u002Fbookings\u002F{id}\u002Fcheck-inbookings:writeMark arrivalPOST\u002Fbookings\u002F{id}\u002Fno-showbookings:writeMark absence",{"id":363,"title":364,"titles":365,"content":366,"level":86},"\u002Fdocs\u002Fapi\u002Fendpoints#catalog","Catalog",[61],"MethodPathScopeGET \u002F POST\u002Fservicesconfig:read \u002F config:writeGET \u002F POST\u002Fresourcesconfig:read \u002F config:writeGET\u002Fresource-groupsconfig:readPOST\u002Fschedules\u002F{id}\u002Foverridesconfig:write",{"id":368,"title":369,"titles":370,"content":371,"level":86},"\u002Fdocs\u002Fapi\u002Fendpoints#customers-and-conversations","Customers and conversations",[61],"MethodPathScopeGET\u002Fcustomerscustomers:readDELETE\u002Fcustomers\u002F{id}customers:read + owner role (anonymizes, LGPD)GET\u002Fconversationsmessages:readGET \u002F POST\u002Fconversations\u002F{id}\u002Fmessagesmessages:read \u002F messages:writePOST\u002Fconversations\u002F{id}\u002Fhandoffmessages:write",{"id":373,"title":374,"titles":375,"content":376,"level":86},"\u002Fdocs\u002Fapi\u002Fendpoints#platform","Platform",[61],"MethodPathScopeGET\u002FmeanyGET \u002F POST\u002Fwebhook-endpointswebhooks:manageGET\u002Feventsbookings:read",{"id":378,"title":379,"titles":380,"content":381,"level":86},"\u002Fdocs\u002Fapi\u002Fendpoints#example-slots","Example: slots",[61],"GET \u002Fv1\u002Fslots?service_id=svc_laser&from=2026-10-20T09:00:00-03:00&to=2026-10-20T20:00:00-03:00&around=2026-10-20T18:00:00-03:00 {\n  \"data\": [\n    { \"start\": \"2026-10-20T17:45:00-03:00\", \"end\": \"2026-10-20T18:30:00-03:00\", \"resource_ids\": [\"res_ana\", \"res_laser1\", \"res_room2\"] },\n    { \"start\": \"2026-10-20T18:45:00-03:00\", \"end\": \"2026-10-20T19:30:00-03:00\", \"resource_ids\": [\"res_carla\", \"res_laser1\", \"res_room1\"] }\n  ],\n  \"unavailable_reason\": null\n}",{"id":383,"title":384,"titles":385,"content":386,"level":86},"\u002Fdocs\u002Fapi\u002Fendpoints#example-booking-object","Example: booking object",[61],"{\n  \"id\": \"bkg_7Qx1\",\n  \"status\": \"confirmed\",\n  \"service_id\": \"svc_laser\",\n  \"start\": \"2026-10-20T17:45:00-03:00\",\n  \"end\": \"2026-10-20T18:30:00-03:00\",\n  \"party_size\": 1,\n  \"customer\": { \"id\": \"cus_31\", \"name\": \"Marina\", \"phone\": \"+5521988887777\", \"locale\": \"pt\" },\n  \"allocations\": [\n    { \"resource_id\": \"res_ana\", \"start\": \"2026-10-20T17:45:00-03:00\", \"end\": \"2026-10-20T18:40:00-03:00\", \"units\": 1 },\n    { \"resource_id\": \"res_laser1\", \"start\": \"2026-10-20T17:45:00-03:00\", \"end\": \"2026-10-20T18:40:00-03:00\", \"units\": 1 },\n    { \"resource_id\": \"res_room2\", \"start\": \"2026-10-20T17:45:00-03:00\", \"end\": \"2026-10-20T18:40:00-03:00\", \"units\": 1 }\n  ],\n  \"payment\": { \"status\": \"paid\", \"amount_cents\": 5000 },\n  \"source\": \"whatsapp\",\n  \"created_at\": \"2026-10-19T11:02:13-03:00\"\n} Allocations include the buffer time (here 10 minutes after the service). html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"id":66,"title":65,"titles":388,"content":389,"level":80},[],"Signed events for bookings, payments and conversations, with automatic retries.",{"id":391,"title":392,"titles":393,"content":394,"level":86},"\u002Fdocs\u002Fapi\u002Fwebhooks#events","Events",[65],"EventWhenbooking.createdA hold or booking was createdbooking.confirmedA booking was confirmed (including after payment)booking.cancelledCancelled or expiredbooking.rescheduledMoved to a new time (payload has the old and new ids)booking.no_showMarked as absentpayment.paidA Pix deposit was receivedmessage.receivedA customer message arrivedconversation.handoffA conversation was handed to a person Create endpoints in Developers → Webhooks or via POST \u002Fv1\u002Fwebhook-endpoints. The signing secret is shown once.",{"id":396,"title":397,"titles":398,"content":399,"level":86},"\u002Fdocs\u002Fapi\u002Fwebhooks#payload","Payload",[65],"{\n  \"id\": \"evt_01J9ZK3T6\",\n  \"type\": \"booking.confirmed\",\n  \"created_at\": \"2026-10-14T15:21:07-03:00\",\n  \"workspace_id\": \"ws_9f2c\",\n  \"data\": { \"booking\": { \"id\": \"bkg_7Qx1\", \"status\": \"confirmed\", \"start\": \"2026-10-15T09:45:00-03:00\" } }\n} Delivery is at least once: use id to ignore duplicates.",{"id":401,"title":402,"titles":403,"content":404,"level":86},"\u002Fdocs\u002Fapi\u002Fwebhooks#verifying-the-signature","Verifying the signature",[65],"Every request has a header: Wagend-Signature: t=1791040867,v1=5c2b9f...e81 v1 is HMAC-SHA256(secret, t + \".\" + raw_body) in hex. Reject requests older than 5 minutes. import crypto from 'node:crypto'\n\nexport function verifyWagend(rawBody: string, header: string, secret: string) {\n  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=') as [string, string]))\n  const age = Math.abs(Date.now() \u002F 1000 - Number(parts.t))\n  if (!parts.t || !parts.v1 || age > 300) return false\n  const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex')\n  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))\n} import hashlib, hmac, time\n\ndef verify_wagend(raw_body: bytes, header: str, secret: str) -> bool:\n    parts = dict(p.split(\"=\", 1) for p in header.split(\",\"))\n    if abs(time.time() - int(parts.get(\"t\", \"0\"))) > 300:\n        return False\n    signed = f\"{parts['t']}.\".encode() + raw_body\n    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, parts.get(\"v1\", \"\"))",{"id":406,"title":407,"titles":408,"content":409,"level":86},"\u002Fdocs\u002Fapi\u002Fwebhooks#retries","Retries",[65],"Respond with any 2xx within 10 seconds. Otherwise we retry after 1 min, 5 min, 30 min, 2 h and 12 h. Every attempt is visible in the delivery log, where you can also resend manually. html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"id":70,"title":69,"titles":411,"content":412,"level":80},[],"Let Claude, ChatGPT or your own agent find slots, book and run a business through the Model Context Protocol. Wagend exposes an MCP server so AI agents can use the same engine as the WhatsApp bot: same holds, same validation, same audit log.",{"id":414,"title":415,"titles":416,"content":417,"level":86},"\u002Fdocs\u002Fmcp#connect","Connect",[69],"URL: https:\u002F\u002Fmcp.wagend.app (Streamable HTTP)Auth: an API key in the Authorization header. OAuth will follow. Example client configuration: {\n  \"mcpServers\": {\n    \"wagend\": {\n      \"type\": \"http\",\n      \"url\": \"https:\u002F\u002Fmcp.wagend.app\",\n      \"headers\": { \"Authorization\": \"Bearer wg_live_...\" }\n    }\n  }\n} Use a wg_test_ key while you experiment.",{"id":419,"title":420,"titles":421,"content":422,"level":86},"\u002Fdocs\u002Fmcp#toolsets","Toolsets",[69],"The tools an agent sees depend on the key's scopes. Booking (slots:read, bookings:write) — for assistants booking on behalf of a customer: ToolDoeslist_servicesServices with duration and pricefind_slotsAvailable slots for a service and date rangehold_slotHold a slot for 10 minutesconfirm_bookingConfirm a hold with customer detailscancel_booking, reschedule_booking, get_bookingManage an existing booking Admin (config:write) — for the owner running the business from their AI assistant: ToolDoeslist_resources, list_todayTeam, rooms, machines and today's agendablock_timeBlock a resource (vacation, maintenance)create_service, update_scheduleChange the catalog and hoursget_statsBookings, occupancy and no-shows",{"id":424,"title":425,"titles":426,"content":427,"level":86},"\u002Fdocs\u002Fmcp#example-prompts","Example prompts",[69],"\"Book me a haircut with Juan tomorrow morning, name Carlos.\"\"Block the laser machine next Monday for maintenance and tell me which bookings are affected.\"\"How many no-shows did we have this month?\"",{"id":429,"title":430,"titles":431,"content":432,"level":86},"\u002Fdocs\u002Fmcp#docs-for-llms","Docs for LLMs",[69],"\u002Fllms.txt lists every documentation page.\u002Fllms-full.txt contains the full English documentation in Markdown. html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":74,"title":73,"titles":434,"content":435,"level":80},[],"How to model a barbershop, an aesthetics clinic, a medical office, a restaurant and deliveries. Each recipe matches a ready-made template you can pick during onboarding.",{"id":437,"title":438,"titles":439,"content":440,"level":86},"\u002Fdocs\u002Frecipes#barbershop","Barbershop",[73],"Each barber is an exclusive staff resource with their own schedule. One group, customer_choice with a round_robin fallback. {\n  \"name\": \"Haircut\",\n  \"duration_min\": 30,\n  \"step_min\": 15,\n  \"buffer_after_min\": 5,\n  \"price_cents\": 5000,\n  \"requirements\": [{ \"resource_group_id\": \"grp_barbers\", \"units\": 1 }]\n}",{"id":442,"title":443,"titles":444,"content":445,"level":86},"\u002Fdocs\u002Frecipes#aesthetics","Aesthetics",[73],"Professionals, machines and rooms are separate resources. A service requires all three at the same time, so the scarcest one (usually the machine) limits the agenda. {\n  \"name\": \"Laser hair removal\",\n  \"duration_min\": 45,\n  \"buffer_after_min\": 10,\n  \"price_cents\": 18000,\n  \"deposit_cents\": 5000,\n  \"requirements\": [\n    { \"resource_group_id\": \"grp_professionals\", \"units\": 1 },\n    { \"resource_group_id\": \"grp_lasers\", \"units\": 1 },\n    { \"resource_group_id\": \"grp_rooms\", \"units\": 1 }\n  ]\n}",{"id":447,"title":448,"titles":449,"content":450,"level":86},"\u002Fdocs\u002Frecipes#clinic","Clinic",[73],"Doctor plus office, per location (one workspace per location). Health data is sensitive: the assistant never asks about symptoms, and the intake form collects only what scheduling needs. {\n  \"name\": \"Appointment\",\n  \"duration_min\": 30,\n  \"buffer_after_min\": 10,\n  \"intake_form\": { \"type\": \"object\", \"properties\": { \"insurance\": { \"type\": \"string\" } } },\n  \"requirements\": [\n    { \"resource_group_id\": \"grp_doctors\", \"units\": 1 },\n    { \"resource_group_id\": \"grp_offices\", \"units\": 1 }\n  ]\n}",{"id":452,"title":453,"titles":454,"content":455,"level":86},"\u002Fdocs\u002Frecipes#restaurant","Restaurant",[73],"The dining room is one pooled resource with 40 units (covers) per slot. Duration grows with the party size, and large parties require a deposit. {\n  \"name\": \"Table\",\n  \"step_min\": 30,\n  \"duration_by_units\": [\n    { \"max_units\": 2, \"duration_min\": 90 },\n    { \"max_units\": 4, \"duration_min\": 120 },\n    { \"max_units\": 20, \"duration_min\": 150 }\n  ],\n  \"requirements\": [{ \"resource_group_id\": \"grp_dining_room\", \"units\": \"party_size\" }]\n} Table mode (specific tables with minimum and maximum size, and combinations) is on the roadmap.",{"id":457,"title":458,"titles":459,"content":460,"level":86},"\u002Fdocs\u002Frecipes#deliveries","Deliveries",[73],"Each zone is a pooled resource with a capacity per 2-hour window. The zone is selected by postal code, and a cutoff rule closes same-day windows at noon. {\n  \"name\": \"Delivery\",\n  \"window_mode\": true,\n  \"duration_min\": 120,\n  \"cutoff_rule\": { \"same_day_until\": \"12:00\" },\n  \"intake_form\": { \"type\": \"object\", \"required\": [\"address\", \"postal_code\"] },\n  \"requirements\": [{ \"resource_group_id\": \"grp_zones\", \"units\": 1, \"select_by\": \"postal_code\" }]\n} html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":462,"title":57,"badge":463,"body":464,"description":328,"extension":920,"meta":921,"navigation":922,"path":58,"rawbody":923,"seo":924,"stem":59,"__hash__":925},"docs_en\u002Fdocs\u002F6.api\u002F2.conventions.md","Draft",{"type":465,"value":466,"toc":913},"minimark",[467,471,543,546,575,578,581,638,641,648,748,886,889,909],[468,469,331],"h2",{"id":470},"basics",[472,473,474,483,490,502,525],"ul",{},[475,476,477,478,482],"li",{},"Base URL ",[479,480,481],"code",{},"https:\u002F\u002Fapi.wagend.app\u002Fv1",". Breaking changes ship as a new version.",[475,484,485,486,489],{},"JSON in and out (",[479,487,488],{},"Content-Type: application\u002Fjson",").",[475,491,492,493,497,498,501],{},"Timestamps in ISO 8601 ",[494,495,496],"strong",{},"with offset"," (",[479,499,500],{},"2026-10-15T09:45:00-03:00","). Stored in UTC.",[475,503,504,505,508,509,497,512,515,516,515,519,515,522,489],{},"Money in integer ",[494,506,507],{},"cents"," plus ",[479,510,511],{},"currency",[479,513,514],{},"BRL",", ",[479,517,518],{},"ARS",[479,520,521],{},"PYG",[479,523,524],{},"USD",[475,526,527,528,515,531,515,534,515,537,515,540,489],{},"IDs are opaque strings with a prefix (",[479,529,530],{},"bkg_",[479,532,533],{},"svc_",[479,535,536],{},"res_",[479,538,539],{},"grp_",[479,541,542],{},"cus_",[468,544,336],{"id":545},"idempotency",[547,548,549,515,552,515,555,558,559,562,563,566,567,570,571,574],"p",{},[479,550,551],{},"POST \u002Fholds",[479,553,554],{},"POST \u002Fholds\u002F{id}\u002Fconfirm",[479,556,557],{},"POST \u002Fbookings"," and ",[479,560,561],{},"POST \u002Fbookings\u002F{id}\u002Freschedule"," ",[494,564,565],{},"require"," an ",[479,568,569],{},"Idempotency-Key"," header (for example a UUID). Repeating a request with the same key within 24 hours returns the original response. Reusing a key with a different body returns ",[479,572,573],{},"422",".",[468,576,341],{"id":577},"pagination",[547,579,580],{},"List endpoints use cursors:",[582,583,588],"pre",{"className":584,"code":585,"language":586,"meta":587,"style":587},"language-bash shiki shiki-themes github-light github-dark","curl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50\" -H \"Authorization: Bearer $WAGEND_KEY\"\n# → { \"data\": [...], \"next_cursor\": \"eyJpZCI6...\" }\ncurl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50&cursor=eyJpZCI6...\" -H \"Authorization: Bearer $WAGEND_KEY\"\n","bash","",[479,589,590,616,622],{"__ignoreMap":587},[591,592,594,598,602,606,609,613],"span",{"class":593,"line":80},"line",[591,595,597],{"class":596},"sScJk","curl",[591,599,601],{"class":600},"sZZnC"," \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50\"",[591,603,605],{"class":604},"sj4cs"," -H",[591,607,608],{"class":600}," \"Authorization: Bearer ",[591,610,612],{"class":611},"sVt8B","$WAGEND_KEY",[591,614,615],{"class":600},"\"\n",[591,617,618],{"class":593,"line":86},[591,619,621],{"class":620},"sJ8bj","# → { \"data\": [...], \"next_cursor\": \"eyJpZCI6...\" }\n",[591,623,625,627,630,632,634,636],{"class":593,"line":624},3,[591,626,597],{"class":596},[591,628,629],{"class":600}," \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50&cursor=eyJpZCI6...\"",[591,631,605],{"class":604},[591,633,608],{"class":600},[591,635,612],{"class":611},[591,637,615],{"class":600},[468,639,346],{"id":640},"errors",[547,642,643,644,647],{},"Errors follow RFC 9457 (",[479,645,646],{},"application\u002Fproblem+json","):",[582,649,653],{"className":650,"code":651,"language":652,"meta":587,"style":587},"language-json shiki shiki-themes github-light github-dark","{\n  \"type\": \"https:\u002F\u002Fwagend.app\u002Fdocs\u002Fapi\u002Fconventions#errors\",\n  \"title\": \"Slot no longer available\",\n  \"status\": 409,\n  \"code\": \"slot_taken\",\n  \"alternatives\": [{ \"start\": \"2026-10-15T10:15:00-03:00\", \"end\": \"2026-10-15T10:45:00-03:00\" }]\n}\n","json",[479,654,655,660,674,686,699,712,742],{"__ignoreMap":587},[591,656,657],{"class":593,"line":80},[591,658,659],{"class":611},"{\n",[591,661,662,665,668,671],{"class":593,"line":86},[591,663,664],{"class":604},"  \"type\"",[591,666,667],{"class":611},": ",[591,669,670],{"class":600},"\"https:\u002F\u002Fwagend.app\u002Fdocs\u002Fapi\u002Fconventions#errors\"",[591,672,673],{"class":611},",\n",[591,675,676,679,681,684],{"class":593,"line":624},[591,677,678],{"class":604},"  \"title\"",[591,680,667],{"class":611},[591,682,683],{"class":600},"\"Slot no longer available\"",[591,685,673],{"class":611},[591,687,689,692,694,697],{"class":593,"line":688},4,[591,690,691],{"class":604},"  \"status\"",[591,693,667],{"class":611},[591,695,696],{"class":604},"409",[591,698,673],{"class":611},[591,700,702,705,707,710],{"class":593,"line":701},5,[591,703,704],{"class":604},"  \"code\"",[591,706,667],{"class":611},[591,708,709],{"class":600},"\"slot_taken\"",[591,711,673],{"class":611},[591,713,715,718,721,724,726,729,731,734,736,739],{"class":593,"line":714},6,[591,716,717],{"class":604},"  \"alternatives\"",[591,719,720],{"class":611},": [{ ",[591,722,723],{"class":604},"\"start\"",[591,725,667],{"class":611},[591,727,728],{"class":600},"\"2026-10-15T10:15:00-03:00\"",[591,730,515],{"class":611},[591,732,733],{"class":604},"\"end\"",[591,735,667],{"class":611},[591,737,738],{"class":600},"\"2026-10-15T10:45:00-03:00\"",[591,740,741],{"class":611}," }]\n",[591,743,745],{"class":593,"line":744},7,[591,746,747],{"class":611},"}\n",[749,750,751,768],"table",{},[752,753,754],"thead",{},[755,756,757,762,765],"tr",{},[758,759,760],"th",{},[479,761,479],{},[758,763,764],{},"Status",[758,766,767],{},"When",[769,770,771,785,798,811,824,836,848,861,873],"tbody",{},[755,772,773,779,782],{},[774,775,776],"td",{},[479,777,778],{},"invalid_request",[774,780,781],{},"400 \u002F 422",[774,783,784],{},"Validation failed",[755,786,787,792,795],{},[774,788,789],{},[479,790,791],{},"unauthorized",[774,793,794],{},"401",[774,796,797],{},"Missing, invalid or revoked key",[755,799,800,805,808],{},[774,801,802],{},[479,803,804],{},"insufficient_scope",[774,806,807],{},"403",[774,809,810],{},"Key lacks the scope",[755,812,813,818,821],{},[774,814,815],{},[479,816,817],{},"not_found",[774,819,820],{},"404",[774,822,823],{},"Unknown id (or belongs to another workspace)",[755,825,826,831,833],{},[774,827,828],{},[479,829,830],{},"slot_taken",[774,832,696],{},[774,834,835],{},"Someone else got the slot",[755,837,838,843,845],{},[774,839,840],{},[479,841,842],{},"invalid_transition",[774,844,696],{},[774,846,847],{},"For example confirming a cancelled booking",[755,849,850,855,858],{},[774,851,852],{},[479,853,854],{},"hold_expired",[774,856,857],{},"410",[774,859,860],{},"The hold expired before confirm",[755,862,863,868,870],{},[774,864,865],{},[479,866,867],{},"idempotency_mismatch",[774,869,573],{},[774,871,872],{},"Same key, different body",[755,874,875,880,883],{},[774,876,877],{},[479,878,879],{},"rate_limited",[774,881,882],{},"429",[774,884,885],{},"Too many requests",[468,887,351],{"id":888},"rate-limits",[547,890,891,892,515,895,558,898,901,902,904,905,908],{},"Limits apply per key and per workspace. Every response includes ",[479,893,894],{},"RateLimit-Limit",[479,896,897],{},"RateLimit-Remaining",[479,899,900],{},"RateLimit-Reset",". On ",[479,903,882],{},", wait ",[479,906,907],{},"Retry-After"," seconds.",[910,911,912],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":587,"searchDepth":86,"depth":624,"links":914},[915,916,917,918,919],{"id":470,"depth":86,"text":331},{"id":545,"depth":86,"text":336},{"id":577,"depth":86,"text":341},{"id":640,"depth":86,"text":346},{"id":888,"depth":86,"text":351},"md",{},true,"---\ntitle: Conventions\ndescription: Formats, idempotency, pagination, errors and rate limits.\nbadge: Draft\n---\n\n## Basics\n\n- Base URL `https:\u002F\u002Fapi.wagend.app\u002Fv1`. Breaking changes ship as a new version.\n- JSON in and out (`Content-Type: application\u002Fjson`).\n- Timestamps in ISO 8601 **with offset** (`2026-10-15T09:45:00-03:00`). Stored in UTC.\n- Money in integer **cents** plus `currency` (`BRL`, `ARS`, `PYG`, `USD`).\n- IDs are opaque strings with a prefix (`bkg_`, `svc_`, `res_`, `grp_`, `cus_`).\n\n## Idempotency\n\n`POST \u002Fholds`, `POST \u002Fholds\u002F{id}\u002Fconfirm`, `POST \u002Fbookings` and `POST \u002Fbookings\u002F{id}\u002Freschedule` **require** an `Idempotency-Key` header (for example a UUID). Repeating a request with the same key within 24 hours returns the original response. Reusing a key with a different body returns `422`.\n\n## Pagination\n\nList endpoints use cursors:\n\n```bash\ncurl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50\" -H \"Authorization: Bearer $WAGEND_KEY\"\n# → { \"data\": [...], \"next_cursor\": \"eyJpZCI6...\" }\ncurl \"https:\u002F\u002Fapi.wagend.app\u002Fv1\u002Fbookings?limit=50&cursor=eyJpZCI6...\" -H \"Authorization: Bearer $WAGEND_KEY\"\n```\n\n## Errors\n\nErrors follow RFC 9457 (`application\u002Fproblem+json`):\n\n```json\n{\n  \"type\": \"https:\u002F\u002Fwagend.app\u002Fdocs\u002Fapi\u002Fconventions#errors\",\n  \"title\": \"Slot no longer available\",\n  \"status\": 409,\n  \"code\": \"slot_taken\",\n  \"alternatives\": [{ \"start\": \"2026-10-15T10:15:00-03:00\", \"end\": \"2026-10-15T10:45:00-03:00\" }]\n}\n```\n\n| `code` | Status | When |\n| --- | --- | --- |\n| `invalid_request` | 400 \u002F 422 | Validation failed |\n| `unauthorized` | 401 | Missing, invalid or revoked key |\n| `insufficient_scope` | 403 | Key lacks the scope |\n| `not_found` | 404 | Unknown id (or belongs to another workspace) |\n| `slot_taken` | 409 | Someone else got the slot |\n| `invalid_transition` | 409 | For example confirming a cancelled booking |\n| `hold_expired` | 410 | The hold expired before confirm |\n| `idempotency_mismatch` | 422 | Same key, different body |\n| `rate_limited` | 429 | Too many requests |\n\n## Rate limits\n\nLimits apply per key and per workspace. Every response includes `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`. On `429`, wait `Retry-After` seconds.\n",{"title":57,"description":328},"2hWwLP3nBlC_RQaDNw9xZl_XFKtNUWAJpQxox_3FCWI",[927,928],{"title":53,"path":54,"stem":55,"description":310,"children":-1},{"title":61,"path":62,"stem":63,"description":929,"children":-1},"Every v1 endpoint at a glance, with the most common request and response shapes.",1790796586035]