OpenReserve for AI agents
An AI assistant can work in a OpenReserve workspace for the person it helps. A workspace owner or admin creates an agent key in Settings, API keys; the agent sends it with every request and can only do what the key allows.
OpenReserve API for agents
Contract 3.2.0. Base URL: https://api.openreserve.app. OpenAPI: https://api.openreserve.app/v1/agent/openapi.json
Connecting
A person who administers a tenant creates an agent key in the dashboard (API keys → kind "agent", with the permissions you need) and gives it to you. Send it on every call as Authorization: Bearer ak_…. Never print it or put it in a URL. An agent key acts for one tenant, only within its permissions, and only on the operations listed here.
Conventions
- JSON in and out. Ids are prefixed (
ten_…,usr_…). Times are ISO-8601 in UTC. - Lists take
?cursor=&limit=and answer{ items, nextCursor }; passnextCursorback for the next page. - Send a new
Idempotency-Key(a UUID) with every POST, PATCH and DELETE. Retrying with the same key never repeats the action. - Errors answer
{ "error": { "code", "message", "outcome", "remediation" } }.outcome: not_committedmeans nothing changed.
Ask the person first
PATCH /v1/tenants/{tenantId}— Update a tenant's settingsDELETE /v1/tenants/{tenantId}/media/{mediaId}— Delete a filePOST /v1/tenants/{tenantId}/domains— Connect a custom domainDELETE /v1/tenants/{tenantId}/domains/{domainId}— Disconnect a custom domainPOST /v1/tenants/{tenantId}/payments/charges— Take a payment on the tenant's account (direct charge)POST /v1/tenants/{tenantId}/payments/charges/{paymentId}/refunds— Refund a payment, fully or in partDELETE /v1/tenants/{tenantId}/notes/{noteId}— Delete a notePOST /v1/tenants/{tenantId}/booking/locations— Add a locationPATCH /v1/tenants/{tenantId}/booking/locations/{locationId}— Change a locationPOST /v1/tenants/{tenantId}/booking/services— Add a servicePATCH /v1/tenants/{tenantId}/booking/services/{serviceId}— Change a serviceDELETE /v1/tenants/{tenantId}/booking/services/{serviceId}— Remove a service from the menuPOST /v1/tenants/{tenantId}/booking/staff— Add a staff memberPATCH /v1/tenants/{tenantId}/booking/staff/{staffId}— Change a staff member (profile, login link, services performed)DELETE /v1/tenants/{tenantId}/booking/staff/{staffId}— Archive a staff member who leftPATCH /v1/tenants/{tenantId}/booking/policy— Change the shop's booking policyPUT /v1/tenants/{tenantId}/booking/staff/{staffId}/hours— Replace a staff member's weekly hoursPUT /v1/tenants/{tenantId}/booking/locations/{locationId}/hours— Replace a location's opening hoursPOST /v1/tenants/{tenantId}/booking/time-off— Add time off (a staff member) or a closure (a location)DELETE /v1/tenants/{tenantId}/booking/time-off/{timeOffId}— Remove time off or a closurePOST /v1/tenants/{tenantId}/booking/appointments— Book a held time for a clientPOST /v1/tenants/{tenantId}/booking/appointments/{appointmentId}/reschedule— Move an appointment to another time (or staff member)POST /v1/tenants/{tenantId}/booking/appointments/{appointmentId}/cancel— Cancel an appointmentPOST /v1/tenants/{tenantId}/booking/clients— Create a client recordPATCH /v1/tenants/{tenantId}/booking/clients/{clientId}— Update client contact details or private notes
Operations
GET /v1/health— Check that the API is up. When: To check that the API is reachable and which contract version it serves.GET /v1/app-config— Get client configuration (versions, features, sign-in client ids). When: Rarely: it describes app versions and sign-in client ids for the official apps.GET /v1/public/tenants/{slug}— Get a tenant's public profile by slug. When: To read a tenant's public name and description from its slug.GET /v1/public/domains/{host}— Find the tenant behind a verified custom domain. When: Rarely: it maps a custom domain to the tenant slug.GET /v1/tenants/{tenantId}— Get a tenant. When: To read the tenant's name, slug, time zone and locale.PATCH /v1/tenants/{tenantId}— Update a tenant's settings. When: When the person asks to rename the tenant or change its time zone or locale. Permission:tenant.manage. Ask the person first.GET /v1/tenants/{tenantId}/members— List a tenant's members. When: To see who is on the team and their roles. Permission:members.view.GET /v1/tenants/{tenantId}/roles— List the roles of a tenant (platform and vertical roles). When: To explain what each role can do, or before suggesting a role change. Permission:members.view.GET /v1/tenants/{tenantId}/invites— List pending invites. When: To see who has been invited but has not joined yet. Permission:members.view.GET /v1/tenants/{tenantId}/audit-log— Read a tenant's audit log (newest first). When: When the person asks who changed something, or what happened recently. Permission:audit.view.POST /v1/tenants/{tenantId}/media/uploads— Start an upload (returns a presigned POST). When: To store a file for the workspace (first step; then send the file and call media.complete). Not when: Never make a file public unless the person asked for a public file (logo, product photo). Permission:media.manage.POST /v1/tenants/{tenantId}/media/uploads/{uploadId}/complete— Finish an upload: check the file and store it. When: After sending the file to the upload URL. Permission:media.manage.GET /v1/tenants/{tenantId}/media— List the workspace's files (newest first). When: To find a file the person mentions. Permission:media.view.GET /v1/tenants/{tenantId}/media/{mediaId}— Get a file (with a fresh link for private files). When: To get a link to one file. Permission:media.view.DELETE /v1/tenants/{tenantId}/media/{mediaId}— Delete a file. When: When the person explicitly asks to delete a file. Permission:media.manage. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/domains— List the workspace's custom domains. When: To see which custom domains are connected and what DNS they still need. Permission:settings.manage.POST /v1/tenants/{tenantId}/domains— Connect a custom domain. When: When the person wants their public pages on a domain they own. Not when: DNS changes happen at the domain owner’s provider: say what to add, then verify. Permission:settings.manage. Ask the person first.POST /v1/tenants/{tenantId}/domains/{domainId}/verify— Check the DNS records of a custom domain. When: After the person says they added the DNS records. Permission:settings.manage.DELETE /v1/tenants/{tenantId}/domains/{domainId}— Disconnect a custom domain. When: When the person explicitly asks to disconnect a domain. Permission:settings.manage. Ask the person first. Cannot be undone.GET /v1/agent/guide— The guide for AI agents (markdown by default, JSON with ?format=json). When: First: it explains how to connect, what each operation is for, and when to ask the person.GET /v1/agent/openapi.json— OpenAPI 3.1 document of the operations an agent may call. When: To generate tool definitions;x-agent-*fields say when to ask the person first.GET /v1/billing/plans— List the plans tenants can subscribe to. When: To compare plans, their prices and what each one includes.GET /v1/tenants/{tenantId}/billing/subscription— Get the tenant's plan, subscription status and entitlements. When: To check the tenant's plan, whether it is paid up, and what it includes.GET /v1/tenants/{tenantId}/payments/account— Get the tenant's payments account and whether it can take payments. When: To check whether the tenant can take payments, or what Stripe still needs. Permission:payments.manage.POST /v1/tenants/{tenantId}/payments/customers— Add one of the tenant's customers (to keep a card on file). When: Before saving a card for a customer of the tenant, when your vertical has none for them yet. Permission:payments.charge. Cannot be undone.GET /v1/tenants/{tenantId}/payments/customers/{customerId}— Get a customer and their card on file. When: To see whether a customer has a card on file before charging it. Permission:payments.charge.POST /v1/tenants/{tenantId}/payments/charges— Take a payment on the tenant's account (direct charge). When: When the person asks to charge a customer, e.g. a no-show fee to the card on file. Not when: Never to charge a card the customer did not agree to have charged. Permission:payments.charge. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/payments/charges/{paymentId}— Get a payment. When: To check whether a payment succeeded, failed or was refunded. Permission:payments.charge.POST /v1/tenants/{tenantId}/payments/charges/{paymentId}/refunds— Refund a payment, fully or in part. When: When the person asks to refund a payment. Permission:payments.refund. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/notes— List notes (newest first). When: To find the team's notes before reading or changing one. Permission:notes.view.POST /v1/tenants/{tenantId}/notes— Create a note. When: When the person asks you to write something down for the team. Permission:notes.manage.GET /v1/tenants/{tenantId}/notes/{noteId}— Get a note. When: To read one note in full. Permission:notes.view.PATCH /v1/tenants/{tenantId}/notes/{noteId}— Change a note. When: When the person asks you to edit a note. Not when: Never to rewrite a note the person did not ask about: earlier text is not kept. Permission:notes.manage. Cannot be undone.DELETE /v1/tenants/{tenantId}/notes/{noteId}— Delete a note. When: When the person explicitly asks to delete a note. Permission:notes.manage. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/booking/locations— List the shop's locations. When: To find a location id or a location time zone. Permission:booking.view.POST /v1/tenants/{tenantId}/booking/locations— Add a location. When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.PATCH /v1/tenants/{tenantId}/booking/locations/{locationId}— Change a location. When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.GET /v1/tenants/{tenantId}/booking/services— List services (the menu), including ones not offered online. When: To find a service id, its duration, price and who performs it. Permission:booking.view.POST /v1/tenants/{tenantId}/booking/services— Add a service. When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.PATCH /v1/tenants/{tenantId}/booking/services/{serviceId}— Change a service. When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.DELETE /v1/tenants/{tenantId}/booking/services/{serviceId}— Remove a service from the menu. When: When the owner explicitly removes a service. Permission:booking.setup.manage. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/booking/staff— List staff members. When: To find a staff id and what they perform. Permission:booking.view.POST /v1/tenants/{tenantId}/booking/staff— Add a staff member. When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.PATCH /v1/tenants/{tenantId}/booking/staff/{staffId}— Change a staff member (profile, login link, services performed). When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.DELETE /v1/tenants/{tenantId}/booking/staff/{staffId}— Archive a staff member who left. When: When the owner says someone left. Permission:booking.setup.manage. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/booking/policy— Get the shop's booking policy. When: To explain notice, cancellation, reminder or fee rules. Permission:booking.view.PATCH /v1/tenants/{tenantId}/booking/policy— Change the shop's booking policy. When: When the owner asks you to change how the shop is set up. Permission:booking.setup.manage. Ask the person first.GET /v1/tenants/{tenantId}/booking/staff/{staffId}/hours— Get a staff member's weekly hours. When: To see when someone works each week. Permission:booking.view.PUT /v1/tenants/{tenantId}/booking/staff/{staffId}/hours— Replace a staff member's weekly hours. When: When the owner tells you when someone works, is away, or when the shop is closed. Permission:booking.schedule.manage. Ask the person first.GET /v1/tenants/{tenantId}/booking/locations/{locationId}/hours— Get a location's opening hours. When: To see when the shop is open. Permission:booking.view.PUT /v1/tenants/{tenantId}/booking/locations/{locationId}/hours— Replace a location's opening hours. When: When the owner tells you when someone works, is away, or when the shop is closed. Permission:booking.schedule.manage. Ask the person first.GET /v1/tenants/{tenantId}/booking/time-off— List time off and closures overlapping some dates. When: To see who is away or when the shop is closed. Permission:booking.view.POST /v1/tenants/{tenantId}/booking/time-off— Add time off (a staff member) or a closure (a location). When: When the owner tells you when someone works, is away, or when the shop is closed. Permission:booking.schedule.manage. Ask the person first.DELETE /v1/tenants/{tenantId}/booking/time-off/{timeOffId}— Remove time off or a closure. When: When the owner tells you when someone works, is away, or when the shop is closed. Permission:booking.schedule.manage. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/booking/availability— Open slots for a service, for the front desk. When: Before booking or moving an appointment, to find a free time. Permission:booking.view.POST /v1/tenants/{tenantId}/booking/holds— Hold a time while the front desk enters the client. When: First step of booking for the shop: hold the time, then book it with the client. Permission:booking.appointments.manage.POST /v1/tenants/{tenantId}/booking/appointments— Book a held time for a client. When: After createHold, once you have the client and the shop agreed to the booking. Permission:booking.appointments.manage. Ask the person first.GET /v1/tenants/{tenantId}/booking/appointments— Appointments starting on some dates (the calendar). When: To read the shop's calendar for a day or a week. Permission:booking.view.GET /v1/tenants/{tenantId}/booking/appointments/{appointmentId}— Get an appointment. When: To read one appointment and its current version. Permission:booking.view.POST /v1/tenants/{tenantId}/booking/appointments/{appointmentId}/reschedule— Move an appointment to another time (or staff member). When: When the shop or the client asks to move a booking (check availability first). Permission:booking.appointments.manage. Ask the person first.POST /v1/tenants/{tenantId}/booking/appointments/{appointmentId}/cancel— Cancel an appointment. When: When asked to cancel a booking. Permission:booking.appointments.manage. Ask the person first. Cannot be undone.GET /v1/tenants/{tenantId}/booking/customers— Search the shop's clients. When: To find an existing client before booking for them. Permission:booking.clients.view.GET /v1/public/tenants/{slug}/booking/locations— A shop's locations open for online booking. When: To tell someone where a shop is and which time zone it books in.GET /v1/public/tenants/{slug}/booking/services— A shop's services offered online, and its booking policy. When: To tell someone what a shop offers, for how long and at what price.GET /v1/public/tenants/{slug}/booking/staff— A shop's staff who take online bookings. When: To tell someone who works at a shop and what each person does.GET /v1/public/tenants/{slug}/booking/availability— Open slots for a service. When: To find when a service can be booked.GET /v1/tenants/{tenantId}/booking/clients— List or search the shop's client directory. When: To find a client when acting for the shop. Permission:booking.clients.view.GET /v1/tenants/{tenantId}/booking/clients/{clientId}— Read one client and their booking history. When: To read a client and their prior bookings for the shop. Permission:booking.clients.view.POST /v1/tenants/{tenantId}/booking/clients— Create a client record. When: When the shop asks to add a client. Permission:booking.clients.manage. Ask the person first. Cannot be undone.PATCH /v1/tenants/{tenantId}/booking/clients/{clientId}— Update client contact details or private notes. When: When the shop asks to correct a client record. Permission:booking.clients.manage. Ask the person first.
Errors
| Code | Status | Meaning | What to do |
|---|---|---|---|
VALIDATION_FAILED | 400 | The request is invalid. | Fix the fields listed in issues and send the request again. |
UNAUTHENTICATED | 401 | Sign-in required. | Send a valid access token or API key in the Authorization header (refresh the session if it expired). |
FORBIDDEN | 403 | You do not have permission to do this. | Ask an owner or admin of the tenant for the permission named in the message. |
TENANT_NOT_FOUND | 404 | Tenant not found. | Check the tenant id; you must be a member of the tenant (or hold one of its API keys). |
IDEMPOTENCY_KEY_REUSED | 422 | This Idempotency-Key was already used for a different request. | Use a new key for a new request; reuse a key only to retry the exact same request. |
RATE_LIMITED | 429 | Too many requests. | Wait the number of seconds in remediation.retryAfterSeconds (or Retry-After) and try again. |
INTERNAL | 500 | Something went wrong on our side. | Re-read the resource to see whether the change happened, then retry with the same Idempotency-Key. |
UNAVAILABLE | 503 | The service is temporarily unavailable. | Retry later with the same Idempotency-Key. |
MEMBER_NOT_FOUND | 404 | Member not found. | List the members and use one of their ids. |
STRIPE_NOT_CONFIGURED | 503 | Stripe is not set up for this environment yet. | The product's operator must store this stage's Stripe keys (docs/billing-and-payments.md); retry after that. |
STRIPE_ERROR | 502 | Stripe could not complete the request. | Retry with the same Idempotency-Key; if it keeps failing, look at the request in the Stripe Dashboard. |
PLAN_UPGRADE_REQUIRED | 402 | The tenant's plan does not include this. | Upgrade the plan (billing.checkout, or the billing portal for a paid plan), then retry. |
PAYMENTS_NOT_ENABLED | 404 | Payments are not part of this product. | Nothing to do: this product does not take payments for its tenants. |
PAYMENTS_ACCOUNT_MISSING | 409 | The tenant has not set up payments yet. | Create the payments account (POST …/payments/account), then finish onboarding in the dashboard. |
PAYMENTS_ACCOUNT_NOT_READY | 409 | The payments account cannot take payments yet. | Finish onboarding in the dashboard: Stripe still needs some details or is reviewing them. |
PAYMENT_CUSTOMER_NOT_FOUND | 404 | Payment customer not found. | Use the id returned when the customer was created. |
PAYMENT_NOT_FOUND | 404 | Payment not found. | Use the id of a payment of this tenant. |
PAYMENT_CARD_MISSING | 409 | This customer has no card on file. | Save a card first (…/card-setup), or take the payment with the customer present. |
PAYMENT_NOT_REFUNDABLE | 409 | This payment cannot be refunded. | Only succeeded payments with an amount left to refund can be refunded. |
MEDIA_TOO_LARGE | 413 | This file is too large. | Images (JPEG, PNG, WebP, GIF) up to 10 MB, PDFs up to 25 MB, videos (MP4, MOV) up to 200 MB. |
UPLOAD_NOT_FOUND | 404 | This upload is unknown or has expired. | Start a new upload (POST …/media/uploads) and send the file within 10 minutes. |
UPLOAD_INCOMPLETE | 409 | The file has not arrived yet. | POST the file to the upload URL first (every field, then the file as file), then complete again. |
MEDIA_REJECTED | 422 | The file is not what the upload announced. | Check the file type and size, then start a new upload with the real values. |
MEDIA_NOT_FOUND | 404 | File not found. | List the files and use one of their ids. |
DOMAIN_NOT_ALLOWED | 400 | This domain cannot be connected. | Use a domain you own, such as book.example.com: not one of ours, an IP address or a *.vercel.app name. |
DOMAIN_TAKEN | 409 | This domain is already connected somewhere else. | Remove it from the workspace that verified ownership or the other Vercel project, then add it again. |
DOMAIN_LIMIT_REACHED | 409 | A workspace can connect at most 10 domains. | Remove a domain you no longer use. |
DOMAIN_NOT_FOUND | 404 | Domain not found. | List the domains and use one of their ids. |
CUSTOM_DOMAINS_UNAVAILABLE | 503 | Custom domains are not available right now. | Try again in a few minutes; if it keeps failing, contact support. |
NOTE_NOT_FOUND | 404 | Note not found. | List the notes and use one of their ids. |
LOCATION_NOT_FOUND | 404 | Location not found. | Locations: List them and use one of their ids. |
SERVICE_NOT_FOUND | 404 | Service not found. | Services: List them and use one of their ids. |
STAFF_NOT_FOUND | 404 | Staff member not found. | Staff: List them and use one of their ids. |
TIME_OFF_NOT_FOUND | 404 | Time off not found. | Time off: List them and use one of their ids. |
CUSTOMER_NOT_FOUND | 404 | Client not found. | Clients: List them and use one of their ids. |
APPOINTMENT_NOT_FOUND | 404 | Appointment not found. | Appointments: List them and use one of their ids. |
HOLD_NOT_FOUND | 404 | This hold does not exist or was released. | Ask for availability again and pick one of the slots it returns. Then create a new hold. |
LOCATION_REQUIRED | 400 | This shop has more than one location: say which one. | Pass locationId (from the locations list). |
SERVICE_NOT_OFFERED | 409 | That staff member does not offer this service online. | Pick a staff member from the service's staffIds, or leave staffId out. |
SLOT_UNAVAILABLE | 409 | That time cannot be booked (outside hours, too soon, too far ahead, or off the booking grid). | Ask for availability again and pick one of the slots it returns. |
SLOT_TAKEN | 409 | Someone else just booked that time. | Ask for availability again and pick one of the slots it returns. |
APPOINTMENT_NOT_CHANGEABLE | 409 | This appointment is not confirmed any more, so it cannot be moved or cancelled. | Read the appointment. Book a new one if the client still wants to come. |
VERSION_CONFLICT | 409 | The appointment changed since you read it. | Read it again and retry with its current version as expectedVersion. |
STAFF_HAS_APPOINTMENTS | 409 | This staff member has upcoming appointments. | Move them to someone else or cancel them, then archive the profile. |
MEMBERSHIP_ALREADY_LINKED | 409 | That login is already linked to another staff profile. | Unlink it from the other profile first (membershipId: null). |
HOURS_OVERLAP | 409 | Weekly hours overlap. | Remove the overlap: one person (or one location) has one window at a time on a weekday. |
BOOKING_HOLD_REFRESH_REQUIRED | 409 | This hold needs to be refreshed before checkout. | Read availability and create a new hold with its holdCapability. |
BOOKING_DEPOSIT_REQUIRED | 409 | A deposit is required before this booking can be confirmed. | Reserve, prepare and start the deposit checkout, then await verified payment finalization. |
CLIENT_CONTACT_CONFLICT | 409 | A client already uses that contact. | Find the existing client or correct the email address or phone number. |
CLIENT_VERSION_CONFLICT | 409 | The client changed since you read it. | Read the client again and retry with its current version as expectedVersion. |