> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gridbeds.com/llms.txt
> Use this file to discover all available pages before exploring further.

# One delivery and every attempt made

<Warning>**Preview.** This operation is not served yet: it is part of the released contract (version 1.0.0). Build against a mock of the contract until the sandbox serves it; see [Testing: mock and sandbox](../../testing).</Warning>


## OpenAPI

````yaml /openapi/distribution-v1.yaml get /webhooks/{webhook_id}/deliveries/{delivery_id}
openapi: 3.1.0
info:
  title: Gridbeds Distribution API
  version: 1.0.0
  summary: >-
    Locations, owners, properties, availability and prices for distribution
    channels.
  description: >
    **Version 1.0.0**, agreed with listing partners over two reviews. An
    operation marked

    `x-gridbeds-availability: planned` is agreed and not served yet; it loses
    the mark when it is served, on the

    sandbox first. From this version v1 only grows: what a channel builds
    against it keeps working.


    **One file, one name.** This document is published as `distribution-v1.yaml`
    and keeps that name for all of v1,

    so two copies can be compared line by line. Which revision a copy is says
    `info.version`, and the contract

    changelog published beside it says what each revision changed.


    Pull for data, push for notifications. A webhook only says what changed;
    read the resource it names. Listing

    reports go the other way: you tell Gridbeds what became of each listing and
    how it performs, and the host sees

    it beside the bookings you send.


    **Ids** are opaque, prefixed, specific to your channel and never reused.

    **Instants** are RFC 3339 UTC seconds ending in `Z`. **Nights** are the
    property's local dates (`timezone`).

    **Money** is `{"amount": <integer ISO 4217 minor units>, "currency":
    "RON"}`. **Text** is a map of BCP 47

    language to plain text and always contains the property's source language:
    `ro` for a property in Romania, the

    property's own language elsewhere (`it` in Italy, `es` in Spain), where `ro`
    is not required. Other languages

    appear once the host has written them. The one exception is a text Gridbeds
    generates rather than the host

    writes, `cancellation.text`: it comes in the languages Gridbeds ships, so it
    is in `en` where the source language

    is not one of them.


    **Lists** return `{data, next_cursor}` (`null` at the end) and accept
    `limit` (1–500, default 100), `cursor`

    and `updated_since`. `updated_since` is inclusive: send back the largest
    `updated_at` you received — no overlap

    is needed — and apply a resource only when its `version` is higher than the
    one you hold. With `updated_since`,

    lists include withdrawn and deleted resources as tombstones `{id, status,
    version, updated_at}`. An

    `updated_since` older than the retention window (35 days) is answered 410
    `sync-window-expired`: resync from

    scratch.


    **One lifecycle for every resource.** Locations, owners, properties,
    amenities and webhook endpoints share one

    `status`, with the same meaning everywhere: `active` (published and in
    effect); `inactive` (published, not in

    effect now: a property that cannot be sold, a location or owner whose every
    published property is inactive, a

    suspended endpoint); `withdrawn` (not published to you now; it may come back
    under the same id with a higher

    version); `deleted` (final; the id is never used again). A `withdrawn` or
    `deleted` resource is a tombstone

    `{id, status, version, updated_at}` and nothing else, on every resource.
    Handle all four values on every

    resource, including a value that resource does not reach today. A property's
    withdrawal and return are

    `property.withdrawn` and `property.distributed`; a location or owner
    changing status (it follows the strongest

    status of the properties naming it) is a `location.updated` or
    `owner.updated` with a higher version, and a

    deletion is `<kind>.deleted`.


    **Every enum is open**: accept values you do not know. v1 only grows; a
    breaking change is `/v2`, announced at

    least 30 days ahead with `Deprecation` and `Sunset` headers.


    **Errors** are RFC 9457 problem details (`application/problem+json`); branch
    on `type` or `code`, never on

    `title`, which follows `Accept-Language`. Every authenticated response,
    refusals included, counts against your

    rate limit and carries the RateLimit fields. 405 and 503 are answered the
    same way on every path.


    **Transport**: HTTPS only, TLS 1.2 or later. A plain-HTTP request is
    refused, never redirected, because its

    bearer key has already crossed the network in clear; treat such a key as
    compromised and roll it.


    **Legal data follows the property's jurisdiction.** What the law asks of a
    listing is declared as data, per

    country and, where the law is regional, per region or municipality.
    `legal.identifiers` lists every legal

    identifier a listing carries, as `{scheme, value, valid_until, issued_by}`.
    A scheme name is lowercase, prefixed

    with the jurisdiction that defines it (`ro.classification`, `it.cin`,
    `eu.str_registration`, `es.ri`), and never

    renamed or reused. **Ignore a scheme you do not know**: schemes are added
    without a new version.

    `legal.classification` and `legal.registration_number` keep the meaning the
    specification gives them: a property

    in Romania is published only with a valid classification certificate and is
    withdrawn (`property.withdrawn`) when

    it expires; a property in Italy is published only with its CIN, as
    `registration_number`, and

    `classification: null`; elsewhere both are `null` unless a jurisdiction's
    rule fills them. Which countries you

    publish is your decision: filter on the location's `address.country`.


    **One property is one room type.** A guesthouse with three room types is
    three properties under one location,

    each with its `units`. The location's `name` and `category` let you show
    them as one listing of a hotel or a

    guesthouse with its room types, or each room type as a listing of its own:
    that choice is yours.


    **Addresses follow the host's choice.** A host decides per location whether
    its exact address is published.

    With `address_visibility: approximate` (the default when the owner is an
    individual, whose address is personal

    data) the location carries no street, number, building or postal code, and
    `geo` is a displaced point within

    `geo.precision_m` metres of the property. The host also chooses that radius:
    200, 500 or 1000 metres, 500 unless

    the host picks another (a dense city centre calls for less than a village).
    The point is fixed for each radius:

    the same on every read while the radius stays, and it cannot be turned back
    into the exact one. The exact

    address is never sent while the location is approximate. A change of
    visibility or of radius is a

    `location.updated` with a higher version, and a change of radius moves the
    point once; replace the address and

    point you hold, and when a location becomes approximate stop showing the
    exact ones you received before.


    **Prices follow the party.** Each night of a rate carries the price for
    every number of adults the property

    takes (`prices_by_occupancy`), what each child adds by age band
    (`child_prices`, the bands named on the rate

    plan) and what an infant adds, all as the guest pays them, so the price of
    any stay is a sum and you compute no

    rule. `price`, `base_occupancy` and `extra_guest_price` keep their meaning
    and are defined from the list.


    **The price of a stay** for a party on one rate plan is, for each night from
    arrival up to departure

    (exclusive): the night's `prices_by_occupancy` entry for the number of
    adults, plus for each child the

    `child_prices` entry of its age band (or `child_price` when the plan names
    no bands), plus `infant_price` for

    each infant; then each fee with `included_in_price: false` and `payable_at:
    booking`, added once for `per: stay`,

    once per night for `per: night` and once per guest and night for `per:
    guest_night` (in v1, the cleaning fee,

    once per stay). That sum is the price of the stay the guest books on the
    property's own site, VAT included

    (however much of it the rate plan takes as a deposit). Nothing else is added
    and nothing is taken off: Gridbeds

    applies no reduction for the length of a stay or for booking early or late.
    A cheaper rate for a longer or an

    earlier stay is a rate plan of its own, with its own nightly prices and
    rules (`min_stay`, `closed_to_arrival`):

    offer every plan whose rules admit the stay, as the property's own site
    does, and the cheapest of them is the

    lowest price Gridbeds offers for it.


    A fee with `payable_at: property` (in v1, the tourist tax, `city_tax`) is
    paid to the property during the stay

    and is not part of that price; the property's own site shows it beside the
    price, and so should you. A fee with

    `included_in_price: true` is already in the nightly prices: show it, and add
    nothing. Charges a property posts to

    a stay on its own account during it (parking, a resort fee, breakfast billed
    by the night) are not published in

    v1; they are not part of the price on the property's own site either, and
    are settled at the property.


    **The tourist tax is never left out.** Some municipalities levy it as a
    fixed amount per guest and night, others

    as a percentage of the accommodation price, and the amounts change by local
    decision. Every property says

    which of three things is true in `city_tax_status`: `levied` (its `fees`
    carry one `city_tax`), `none` (the

    property declares that no tourist tax applies) or `undeclared` (the property
    has not said; one may apply). A

    property without a `city_tax` fee is therefore never a statement that there
    is no tax unless `city_tax_status`

    is `none`. A `city_tax` fee states what is known and no more:


    - `basis: fixed` with `amount`: per taxable guest and night (`per:
    guest_night`). The tax of a stay is `amount` ×
      taxable guests × nights, the nights capped at `max_nights`, rounded once to the currency's minor unit; the
      taxable guests are the adults and the children, or the adults alone when `exempts_children` is true, and never
      the infants. That is the most the property charges: any further exemption it applies (a number of exempt
      guests per stay, for instance) only lowers it. `amount` is the rate in force on the property's today; a stay
      that begins after a new rate takes effect is charged the new one.
    - `basis: percent` with `percent_bp`: a percentage, in basis points (125 is
    1.25%), of the accommodation price
      named by `percent_of`, for the whole stay (`per: stay`). Gridbeds does not compute it and neither should you:
      show the percentage as payable at the property ("local tax 1.25%, paid at the property"). Exemptions from a
      percentage are not modelled: `exempts_children` is null and the property applies its own.
    - A value that is not known is null and the fee is still there: `basis:
    fixed` with `amount: null`, or
      `basis: percent` with `percent_bp: null`, says that the tax exists and how it is charged but not how much;
      `basis: null` (with `amount` and `percent_bp` null) says only that a tourist tax applies. Show such a fee as
      "local tax payable at the property" and never as zero.

    The property's own quote can differ from your sum in four ways, and in no
    other. **A promo code** lowers it for

    a guest who types one on the property's site; the rate plan's
    `lower_price_reasons` says when that can happen,

    and a code is never published. **Rounding**: Gridbeds rounds VAT once over
    the whole stay, so the sum can differ

    by up to one minor unit for each amount you added (each component of each
    night, and each fee), until `/quotes`

    (stage 2) answers the exact figure. **A change of VAT rate** during a stay:
    Gridbeds then charges every night at

    the rate in force on the departure date, while each published night carries
    the rate in force on the morning

    after it. **Your markup**: it applies to the nightly prices you receive and
    never to fees, and the property's

    site shows its prices without it. One more difference holds until the
    property's site asks for each child's age

    and for infants: the redirect carries only the number of adults and of
    children (`url_template`), so a party

    with children on a plan that prices them by age band is priced there without
    their ages, and a party's infants

    are not priced there at all.


    **Availability is published at least 365 nights ahead**, on a window that
    rolls with each property's local

    date: every night from the property's today up to `horizon_end` (exclusive)
    is present, and a night with no rate

    comes with every rate `closed`, never missing. `horizon_end` is at least 365
    nights after the property's today

    and may be further; it is in every availability response.


    **Build against a mock.** A mock server generated from this document answers
    every operation before it is

    served, e.g. `npx @stoplight/prism-cli mock --dynamic distribution-v1.yaml`
    (pin an exact 5.x release); `prism

    proxy` checks the sandbox's answers against the document. Every change is
    recorded in the contract changelog

    published beside it. Generated client libraries follow from the same
    document.


    **Two environments, configured apart.** The sandbox (`gbd_test_` keys) and
    production (`gbd_live_` keys) are

    separate deployments, each with its own record of your channel: keys,
    webhook endpoints and the owner-connect

    redirect URL are registered with each one separately, so the sandbox can
    point at your staging site while

    production points at your live one. Nothing registered in one environment
    exists in the other.


    **Testing webhooks.** A webhook endpoint must be a public HTTPS address: a
    private, loopback, link-local or

    reserved target is refused, so that a registered URL cannot be used to reach
    networks the internet cannot

    (server-side request forgery). A webhook therefore cannot reach a
    developer's machine. Locally, read

    `GET /events`, which carries the same events in commit order, or expose your
    receiver through a public HTTPS

    tunnel; webhooks themselves are exercised against the sandbox.
  contact:
    name: Gridbeds partner support
    email: support@gridbeds.com
  license:
    name: Proprietary
    url: https://gridbeds.com/en/terms/
servers:
  - url: https://api.gridbeds.com/api/v1/distribution
    description: Production (gbd_live_ keys)
  - url: https://staging-api.gridbeds.com/api/v1/distribution
    description: Sandbox (gbd_test_ keys)
security:
  - distributionKey: []
tags:
  - name: Channel
    description: The channel the key belongs to.
  - name: Catalog
    description: Locations, owners, properties and the amenity catalogue.
  - name: Availability
    description: Nightly availability, prices and restrictions.
  - name: Events
    description: The ordered log of what changed, and the webhook notification.
  - name: Webhooks
    description: The channel's webhook endpoints and their deliveries.
  - name: Listings
    description: >
      What the channel reports back about its listings: the publication state of
      each property on your side

      (`PUT /properties/{property_id}/listing`) and its daily statistics (`POST
      /listing-stats`), both with the

      scope `write:listings`. Gridbeds shows them to the host in its dashboard
      (views, clicks to booking, whether a

      listing is published and promoted) and joins them with the bookings that
      arrive from you. Gridbeds sends no

      webhook for them: it is the receiver.
  - name: Owner connect
    description: >
      A host links its Gridbeds listings to an account on your side, and
      Gridbeds shares no e-mail address or other

      contact detail to make it happen.


      1. The host starts "Connect your account" for your channel in Gridbeds.

      2. Gridbeds sends the host's browser to your redirect URL
      (`owner_connect.redirect_url` of `GET /channel`,
         registered with Gridbeds for each environment) as an HTML form POST carrying `token` (the `ownerConnect`
         webhook), so the token never appears in a URL, a server log, the browser history or a `Referer`.
      3. You verify the token, let the host sign in or sign up on your side (the
      host types their own e-mail address
         there if you need one), ask the signed-in host to confirm the link on your own page (showing the owner's
         `display_name`), bind the token's `sub`, an owner id you hold from `GET /owners`, to that account, and
         confirm with `PUT /owners/{owner_id}/connection`. The host then sees the account connected in Gridbeds.
         Never bind on the POST alone: any site can make a browser submit a form, so a token posted without the
         host's confirmation could link someone else's owner to the account that happens to be signed in.

      Nothing about the host reaches you except through that action.


      **The token** is a JWS in compact serialization (RFC 7515; a JWT, RFC
      7519) signed with ES256. Header:

      `alg` `ES256`, `typ` `owner-connect+jwt`, `kid` the signing key. Claims:
      `iss` the API's origin

      (`https://api.gridbeds.com`, or `https://staging-api.gridbeds.com` in the
      sandbox); `aud` your channel, the

      `channel` of `GET /channel`; `sub` the owner id; `property_ids` the ids of
      that owner's properties distributed

      to you when the host started; `iat`; `exp`, at most 300 seconds after
      `iat`; `jti`, a random single-use id

      (128 bits); `locale`, the host's interface language (BCP 47), which may be
      absent.


      **Verify all of it before trusting any claim:** the signature, with the
      key named by `kid` in

      `GET /owner-connect/keys` (a JWKS, RFC 7517; fetch it again on an unknown
      `kid` and cache it as its

      `Cache-Control` says); `alg` exactly `ES256` and `typ` exactly
      `owner-connect+jwt`, refusing `none` and every

      other value (RFC 8725); `iss` and `aud` as above; `exp` in the future and
      `iat` not in the future, with at most

      60 seconds of clock skew; `sub` an owner distributed to you. A new signing
      key is published at least 24 hours

      before it signs, and a retired one stays published 24 hours after it
      stops.


      **Two deadlines.** `exp` (at most 300 seconds) bounds the browser's leg:
      your redirect page accepts the token only

      before it. The confirmation may come later, because the host may have to
      sign up first: `PUT

      /owners/{owner_id}/connection` accepts an unconfirmed token until 3600
      seconds after its `iat`, and only from

      the channel named in its `aud`, with that channel's own key. Keep the
      verified token server-side meanwhile,

      never in a page or a URL.


      **Replay.** A token is single use. Keep every `jti` you accept until its
      `exp` and refuse one you have seen.

      Gridbeds binds a token to the one connection it confirms: confirming it
      again for the same owner answers the

      same connection, and any other use is refused.
paths:
  /webhooks/{webhook_id}/deliveries/{delivery_id}:
    get:
      tags:
        - Webhooks
      summary: One delivery and every attempt made
      operationId: getWebhookDelivery
      parameters:
        - $ref: '#/components/parameters/WebhookIdPath'
        - name: delivery_id
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/DeliveryId'
      responses:
        '200':
          description: The delivery.
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
            Request-Id:
              $ref: '#/components/headers/RequestId'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimitPolicy'
            RateLimit:
              $ref: '#/components/headers/RateLimit'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookDeliveryDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Internal'
      security:
        - distributionKey:
            - manage:webhooks
components:
  parameters:
    WebhookIdPath:
      name: webhook_id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/WebhookId'
  schemas:
    DeliveryId:
      type: string
      pattern: ^dlv_[0-7][0-9a-hjkmnp-tv-z]{25}$
      examples:
        - dlv_2bq8x0vmh47m9scr3d1k6pz0te
    WebhookDeliveryDetail:
      allOf:
        - $ref: '#/components/schemas/WebhookDelivery'
        - type: object
          required:
            - attempts_log
          properties:
            attempts_log:
              type: array
              items:
                type: object
                required:
                  - attempt_number
                  - latency_ms
                  - signed_at
                properties:
                  attempt_number:
                    type: integer
                    minimum: 1
                  response_status:
                    type:
                      - integer
                      - 'null'
                  response_excerpt:
                    type:
                      - string
                      - 'null'
                    maxLength: 1000
                    description: Your own response, truncated.
                  error:
                    oneOf:
                      - $ref: '#/components/schemas/WebhookFailure'
                      - type: 'null'
                  latency_ms:
                    type: integer
                    minimum: 0
                  signed_at:
                    $ref: '#/components/schemas/Instant'
    WebhookId:
      type: string
      pattern: ^whk_[0-7][0-9a-hjkmnp-tv-z]{25}$
      examples:
        - whk_3fksgepbgj9yqv8wccdx8sxs8v
    WebhookDelivery:
      type: object
      required:
        - id
        - event_id
        - type
        - state
        - attempts
        - created_at
      properties:
        id:
          $ref: '#/components/schemas/DeliveryId'
        event_id:
          $ref: '#/components/schemas/EventId'
        type:
          $ref: '#/components/schemas/EventType'
        state:
          $ref: '#/components/schemas/DeliveryState'
        attempts:
          type: integer
          minimum: 0
        next_attempt_at:
          oneOf:
            - $ref: '#/components/schemas/Instant'
            - type: 'null'
        last_response_status:
          type:
            - integer
            - 'null'
        last_error:
          oneOf:
            - $ref: '#/components/schemas/WebhookFailure'
            - type: 'null'
        delivered_at:
          oneOf:
            - $ref: '#/components/schemas/Instant'
            - type: 'null'
        created_at:
          $ref: '#/components/schemas/Instant'
    WebhookFailure:
      type: string
      enum:
        - timeout
        - dns_failure
        - connection_refused
        - tls_error
        - connection_failed
        - blocked_unsafe_target
        - http_error
        - outbound_channel_closed
        - partner_not_entitled
        - endpoint_suspended
        - endpoint_failing
      description: Open enum.
    Instant:
      type: string
      format: date-time
      pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$
      examples:
        - '2026-10-16T09:30:00Z'
    Problem:
      type: object
      required:
        - type
        - title
        - status
        - code
        - instance
        - request_id
      additionalProperties: true
      properties:
        type:
          type: string
          format: uri
          description: >-
            https://api.gridbeds.com/problems/{slug}; resolves to a page
            describing the problem.
          examples:
            - https://api.gridbeds.com/problems/not-found
          x-gridbeds-slugs:
            - invalid-parameters
            - invalid-cursor
            - window-beyond-horizon
            - unauthorized
            - insufficient-scope
            - partner-suspended
            - not-found
            - method-not-allowed
            - conflict
            - sync-window-expired
            - event-cursor-expired
            - unsupported-media-type
            - rate-limited
            - internal
            - service-unavailable
        title:
          type: string
        status:
          type: integer
          minimum: 400
          maximum: 599
        detail:
          type: string
        instance:
          type: string
          format: uri
          description: urn:uuid:<Request-Id>
        code:
          $ref: '#/components/schemas/ProblemCode'
        request_id:
          type: string
          format: uuid
    ScopeProblem:
      allOf:
        - $ref: '#/components/schemas/Problem'
        - type: object
          required:
            - required_scopes
          properties:
            required_scopes:
              type: array
              items:
                $ref: '#/components/schemas/Scope'
    EventId:
      type: string
      pattern: ^evt_(test_)?[0-9a-hjkmnp-tv-z]{13}$
      examples:
        - evt_00000000000ab
      description: Opaque. Ordered within your channel; do not parse.
    EventType:
      type: string
      enum:
        - location.created
        - location.updated
        - location.deleted
        - owner.created
        - owner.updated
        - owner.deleted
        - property.created
        - property.updated
        - property.deleted
        - property.distributed
        - property.withdrawn
        - availability.updated
      description: Open enum.
    DeliveryState:
      type: string
      enum:
        - pending
        - delivered
        - failed
        - cancelled
      description: >-
        Open enum. A delivery is a log record, not a resource: it has a state
        rather than the resource status, and is pruned with the log after 35
        days.
    ProblemCode:
      type: string
      description: Open enum. Branch on this or on type.
      enum:
        - invalid_parameters
        - invalid_cursor
        - window_beyond_horizon
        - missing_api_key
        - invalid_api_key
        - api_key_wrong_environment
        - api_key_expired
        - api_key_revoked
        - insufficient_scope
        - partner_suspended
        - not_found
        - method_not_allowed
        - webhook_endpoint_exists
        - webhook_endpoint_limit_reached
        - webhooks_suspended_by_platform
        - webhook_endpoint_still_failing
        - sync_window_expired
        - event_cursor_expired
        - unsupported_media_type
        - rate_limited
        - too_many_failed_authentications
        - internal
        - service_unavailable
    Scope:
      type: string
      enum:
        - read:catalog
        - read:ari
        - manage:webhooks
        - write:listings
      description: >-
        Open enum. write:listings covers what you report back to Gridbeds,
        listing reports and owner connections. write:bookings is reserved.
  headers:
    ETag:
      schema:
        type: string
      description: Weak validator; send it back as If-None-Match for a 304.
    RequestId:
      schema:
        type: string
        format: uuid
      description: Quote it to support; equals the problem's instance.
    RateLimitLimit:
      schema:
        type: integer
    RateLimitRemaining:
      schema:
        type: integer
    RateLimitReset:
      schema:
        type: integer
      description: Seconds until the window resets.
    RateLimitPolicy:
      schema:
        type: string
      description: >-
        draft-ietf-httpapi-ratelimit-headers, e.g. "partner";q=600;w=60
        (provisional; the three RateLimit-* fields above are the stable
        contract).
    RateLimit:
      schema:
        type: string
      description: draft-ietf-httpapi-ratelimit-headers, e.g. "partner";r=598;t=37
    WWWAuthenticate:
      schema:
        type: string
      description: RFC 6750, e.g. Bearer realm="distribution", error="invalid_token"
    RetryAfter:
      schema:
        type: integer
      description: Seconds.
  responses:
    Unauthorized:
      description: >-
        unauthorized. code is missing_api_key, invalid_api_key (malformed or
        unknown — identical), api_key_wrong_environment (from the key's own
        prefix), api_key_expired or api_key_revoked (told apart only for a key
        whose secret matched).
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WWWAuthenticate'
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Forbidden:
      description: >-
        insufficient-scope (with required_scopes) or partner-suspended. Counts
        against the rate limit.
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WWWAuthenticate'
        Request-Id:
          $ref: '#/components/headers/RequestId'
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimitPolicy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
      content:
        application/problem+json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ScopeProblem'
              - $ref: '#/components/schemas/Problem'
    NotFound:
      description: >-
        not-found: does not exist, was never distributed to you, belongs to
        another channel, is malformed, or (availability) is withdrawn or
        deleted. Identical in every case.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimitPolicy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    RateLimited:
      description: >-
        rate-limited (your budget, or repeated failed authentication from one
        address).
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        Request-Id:
          $ref: '#/components/headers/RequestId'
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimitPolicy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Internal:
      description: internal.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    distributionKey:
      type: http
      scheme: bearer
      bearerFormat: gbd_live_… or gbd_test_… (63 characters)
      description: >
        A key starts with `gbd_live_` (production) or `gbd_test_` (sandbox) and
        ends in a CRC32 checksum of its

        secret part, so a mistyped or truncated key is refused before any
        lookup, as is a key of the other

        environment. The prefix is registered with public secret scanners
        (GitHub secret scanning, TruffleHog,

        GitGuardian): a key found in a public repository is revoked at once and
        your technical contact is told.

        Gridbeds stores only a hash of each key. One partner may hold two keys
        per environment during a rotation.

        Operations list the scopes they need; the scopes are OAuth 2.0 scope
        tokens, so short-lived access tokens

        from a client-credentials grant can be offered later beside keys, with
        the same scopes and no change to any

        operation.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.