---
updatedAt: 2026-10-30T21:08:37.000Z
agentTools:
  projectIndex: https://developers.agendapro.com/llms.txt
---

# Cancel Booking

This endpoint cancels a booking.

### Customer policy enforcement

Cancellations are validated against the merchant's customer policy before being applied. If the merchant has disabled cancellations, the booking is too close to its start time, or it has reached its maximum number of changes, the request is rejected with `422 restricted` and the `detail` field identifies which rule tripped:

| **Detail** | **Meaning** |
| --- | --- |
| `can_cancel` | Merchant disabled cancellations via the customer flow. |
| `before_edit_booking` | Booking is within the merchant's pre-start lock window (no cancellations allowed this close to `start_time`). |
| `max_changes` | Booking already reached the merchant's maximum number of changes. |

These values are configured by the merchant in **[Configuraciones > Sitio web > Edición y cancelación de reservas en línea](https://app.agendapro.com/company_settings/bookings)**.

Clients integrating against this API should surface these conditions to their end users as "the merchant does not allow this cancellation" rather than retrying the request.

### Errors Dictionary

| **Status** | **Error** | **Detail** | **Description** |
| --- | --- | --- | --- |
| 400 | invalid_body | expected_object | Request body is not a JSON object. |
| 401 | unauthorized | invalid_api_key | Missing or invalid Bearer token. |
| 401 | unauthorized | api_config_inactive | API access is inactive for this company. |
| 403 | forbidden | scope_denied | API key lacks `bookings:write` scope. |
| 404 | not_found | booking | Booking not found. |
| 422 | restricted | can_cancel | Merchant has disabled cancellations via the customer flow. |
| 422 | restricted | before_edit_booking | Booking is within the merchant's pre-start lock window. |
| 422 | restricted | max_changes | Booking has reached the merchant's maximum number of changes. |
| 429 | rate_limited | burst_limit_exceeded | Per-minute request limit exceeded. |
| 429 | rate_limited | daily_quota_exceeded | Daily request quota exceeded. |
| 502 | upstream_unavailable |  | The upstream service is unavailable. |


# OpenAPI definition

````json
{
  "openapi": "3.0.3",
  "info": {
    "title": "Connect v3",
    "version": "3.0.0",
    "description": "Public API gateway for AgendaPro.\nAuthenticates external developers, enforces rate limits and scopes, and proxies requests to internal services.\n\n## Authentication\n\nAll requests require a Bearer API key in the `Authorization` header:\n\n```\nAuthorization: Bearer <your-api-key>\n```\n\nAPI keys are scoped to a single company. The `company_id` is derived automatically\nfrom the API key — you never pass it as a parameter.\n\n## Scopes\n\nAPI keys may be restricted to specific scopes. Scopes follow the pattern `{resource}:{action}`:\n\n| Scope | Description |\n| --- | --- |\n| `bookings:read` | List and show bookings |\n| `bookings:write` | Create, update, and cancel bookings |\n| `clients:read` | List and show clients |\n| `clients:write` | Create and update clients |\n| `locations:read` | List and show locations |\n| `services:read` | List and show services |\n| `categories:read` | List service categories |\n| `providers:read` | List and show providers |\n| `custom_attributes:read` | List custom attribute templates |\n| `sales:read` | List and show sales |\n| `carts:read` | Show carts |\n| `carts:write` | Create and update carts; create payment requests on a cart |\n| `payment_requests:write` | Cancel payment requests |\n| `leads:read` | List and show Cortex leads and their conversations |\n| `segments:read` | List and show Cortex CRM segments and their members |\n| `conversations:read` | List the messages and the agent tool calls of a Cortex conversation |\n| `reminder_messages:read` | List the reminder messages sent by Cortex |\n| `provider_breaks:read` | List and show provider breaks; show provider break series |\n| `provider_breaks:write` | Create, update and delete provider breaks; create and delete provider break series |\n\nAn API key with empty scopes has full access (all scopes granted).\n\n## Rate Limiting\n\nTwo rate limits apply per company:\n\n- **Burst limit**: Maximum requests per minute.\n- **Daily quota**: Maximum requests per day.\n\nRate limit status is returned in response headers:\n\n| Header | Description |\n| --- | --- |\n| `X-RateLimit-Limit` | Daily quota limit |\n| `X-RateLimit-Remaining` | Daily requests remaining |\n| `X-RateLimit-Reset` | Unix timestamp when daily quota resets |\n| `X-RateLimit-Burst-Limit` | Burst (per-minute) limit |\n| `X-RateLimit-Burst-Remaining` | Burst requests remaining |\n| `X-RateLimit-Burst-Reset` | Unix timestamp when burst window resets |\n\nWhen either limit is exceeded, the API returns `429 Too Many Requests` with a `Retry-After` header.\n\n## Pagination\n\nList endpoints return paginated responses wrapped in a `data` array with a `pagination` metadata object:\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"current_page\": 1,\n    \"per_page\": 30,\n    \"next_page\": null,\n    \"prev_page\": null,\n    \"total_records\": 95,\n    \"total_pages\": 4\n  }\n}\n```\n\nLeads, segments and reminder messages (served by Cortex, AgendaPro's conversational agent) are\npaginated by cursor instead. Pass `limit` and, for the next page, the `next_cursor` you received\nas `cursor`; `next_cursor` is `null` on the last page:\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"next_cursor\": \"eyJpZCI6IjEyMyJ9\"\n  }\n}\n```\n\n## Dates and times\n\nBooking and provider break dates are the local time of the location, written in ISO 8601\nwith a `Z` suffix. The `Z` does **not** mean UTC: a break that starts at 13:00 in Santiago is\nsent and returned as `2026-10-23T13:00:00Z`. Send dates in the same form: an offset such as\n`-03:00` either shifts the time or is rejected, depending on the endpoint.\n\n## Webhooks\n\nThe `booking.created` and `booking.updated` webhook payloads are filtered with the same field list\nas the Booking resource, so they also carry `creative_source`.\n\n## Errors\n\nErrors produced by Connect use the `{error, detail}` format:\n\n```json\n{\n  \"error\": \"error_type\",\n  \"detail\": \"additional_context\"\n}\n```\n\nSome business errors of carts, payment requests, provider breaks and the booking history come from the service behind the endpoint with a legacy single-key code, such as `{\"error\": \"cart_expired\"}`. Each endpoint's errors dictionary says which shape each error has.\n"
  },
  "servers": [
    {
      "url": "https://connect.agendapro.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Bookings",
      "description": "Booking management (list, show, create, update, cancel)"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/v3/bookings/{id}/cancel": {
      "patch": {
        "operationId": "cancelBooking",
        "summary": "Cancel Booking",
        "description": "This endpoint cancels a booking.\n\n### Customer policy enforcement\n\nCancellations are validated against the merchant's customer policy before being applied. If the merchant has disabled cancellations, the booking is too close to its start time, or it has reached its maximum number of changes, the request is rejected with `422 restricted` and the `detail` field identifies which rule tripped:\n\n| **Detail** | **Meaning** |\n| --- | --- |\n| `can_cancel` | Merchant disabled cancellations via the customer flow. |\n| `before_edit_booking` | Booking is within the merchant's pre-start lock window (no cancellations allowed this close to `start_time`). |\n| `max_changes` | Booking already reached the merchant's maximum number of changes. |\n\nThese values are configured by the merchant in **[Configuraciones > Sitio web > Edición y cancelación de reservas en línea](https://app.agendapro.com/company_settings/bookings)**.\n\nClients integrating against this API should surface these conditions to their end users as \"the merchant does not allow this cancellation\" rather than retrying the request.\n\n### Errors Dictionary\n\n| **Status** | **Error** | **Detail** | **Description** |\n| --- | --- | --- | --- |\n| 400 | invalid_body | expected_object | Request body is not a JSON object. |\n| 401 | unauthorized | invalid_api_key | Missing or invalid Bearer token. |\n| 401 | unauthorized | api_config_inactive | API access is inactive for this company. |\n| 403 | forbidden | scope_denied | API key lacks `bookings:write` scope. |\n| 404 | not_found | booking | Booking not found. |\n| 422 | restricted | can_cancel | Merchant has disabled cancellations via the customer flow. |\n| 422 | restricted | before_edit_booking | Booking is within the merchant's pre-start lock window. |\n| 422 | restricted | max_changes | Booking has reached the merchant's maximum number of changes. |\n| 429 | rate_limited | burst_limit_exceeded | Per-minute request limit exceeded. |\n| 429 | rate_limited | daily_quota_exceeded | Daily request quota exceeded. |\n| 502 | upstream_unavailable |  | The upstream service is unavailable. |\n",
        "tags": [
          "Bookings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/BookingId"
          }
        ],
        "responses": {
          "204": {
            "description": "Booking cancelled."
          },
          "400": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Cancellation rejected by the merchant's customer policy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorDetailResponse"
                },
                "examples": {
                  "canCancel": {
                    "summary": "[Error] 422 - Merchant disabled cancellations",
                    "value": {
                      "error": "restricted",
                      "detail": "can_cancel"
                    }
                  },
                  "beforeEditBooking": {
                    "summary": "[Error] 422 - Inside pre-start lock window",
                    "value": {
                      "error": "restricted",
                      "detail": "before_edit_booking"
                    }
                  },
                  "maxChanges": {
                    "summary": "[Error] 422 - Maximum changes reached",
                    "value": {
                      "error": "restricted",
                      "detail": "max_changes"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key issued per company. Pass as Authorization: Bearer <key>."
      }
    },
    "parameters": {
      "BookingId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Booking unique identifier.",
        "schema": {
          "type": "integer",
          "format": "int64",
          "minimum": 1
        }
      }
    },
    "schemas": {
      "ErrorDetailResponse": {
        "type": "object",
        "description": "Standard error response.\nThe `error` field contains the error type and the `detail` field\nprovides additional context.\n",
        "properties": {
          "error": {
            "type": "string",
            "description": "Error type identifier.",
            "example": "unauthorized"
          },
          "detail": {
            "type": "string",
            "nullable": true,
            "description": "Additional context about the error.",
            "example": "invalid_api_key"
          }
        },
        "required": [
          "error"
        ]
      }
    },
    "responses": {
      "InvalidBody": {
        "description": "Request body is not a JSON object.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "invalidBody": {
                "summary": "[Error] 400 Bad Request - Body is not a JSON object",
                "value": {
                  "error": "invalid_body",
                  "detail": "expected_object"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid Bearer API key, or inactive API configuration.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string",
              "example": "Bearer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "invalidApiKey": {
                "summary": "[Error] 401 Unauthorized - Invalid API key",
                "value": {
                  "error": "unauthorized",
                  "detail": "invalid_api_key"
                }
              },
              "apiConfigInactive": {
                "summary": "[Error] 401 Unauthorized - API access inactive",
                "value": {
                  "error": "unauthorized",
                  "detail": "api_config_inactive"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "API key lacks the required scope for this operation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "scopeDenied": {
                "summary": "[Error] 403 Forbidden - Scope denied",
                "value": {
                  "error": "forbidden",
                  "detail": "scope_denied"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "notFound": {
                "summary": "[Error] 404 Not Found - Resource not found",
                "value": {
                  "error": "not_found",
                  "detail": "booking"
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Check the `Retry-After` header.",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the rate limit resets.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "burstLimitExceeded": {
                "summary": "[Error] 429 Too Many Requests - Burst limit",
                "value": {
                  "error": "rate_limited",
                  "detail": "burst_limit_exceeded"
                }
              },
              "dailyQuotaExceeded": {
                "summary": "[Error] 429 Too Many Requests - Daily quota",
                "value": {
                  "error": "rate_limited",
                  "detail": "daily_quota_exceeded"
                }
              }
            }
          }
        }
      },
      "UpstreamUnavailable": {
        "description": "Upstream service is unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "upstreamUnavailable": {
                "summary": "[Error] 502 Bad Gateway - Upstream unavailable",
                "value": {
                  "error": "upstream_unavailable",
                  "detail": "The upstream service is unavailable"
                }
              }
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal server error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorDetailResponse"
            },
            "examples": {
              "internalError": {
                "summary": "[Error] 500 Internal Server Error - Internal error",
                "value": {
                  "error": "internal_error",
                  "detail": "unexpected"
                }
              }
            }
          }
        }
      }
    }
  }
}
````