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

# Retrieve a quote by id



## OpenAPI

````yaml /api-reference/openapi.yaml get /quotes/{quote_id}
openapi: 3.1.0
info:
  title: SafarAPI
  version: 1.0.0
  summary: B2B API for banking partners — Safariat catalog and booking
  description: >
    SafarAPI lets banking partners integrate the Safariat travel catalog into
    their

    authenticated customer areas and book on behalf of their own end customers
    who are

    already authenticated and KYC-verified on the bank side.


    ## Channel model

    - The traveler never authenticates with Safariat.

    - Payment is collected on the banking platform (out-of-band from Safariat).

    - The bank submits the HMAC-signed payment confirmation at booking time.

    - Monthly post-payment settlement (see `GET /settlements`).


    ## Authentication

    Bearer API key in the `Authorization` header. Format
    `sk_live_<prefix>_<secret>` (production)

    or `sk_test_<prefix>_<secret>` (sandbox). The secret is shown only once at
    key generation —

    Safariat stores only an argon2id hash.


    ## Write request signing

    Production write requests (`POST`, `PUT`, `DELETE`) additionally require:

    - `X-Timestamp`: Unix timestamp in seconds, validity window ±5 minutes.

    - `X-Signature`: `hex(HMAC_SHA256(secret,
    "{timestamp}\n{method}\n{path}\n{body}"))`.

    - `Idempotency-Key`: opaque string unique per operation (UUID v4
    recommended),
      retained for 24 h. Replayed responses carry an `Idempotent-Replayed: true` header.

    Request signing applies only to production `sk_live_*` keys. Sandbox
    `sk_test_*` keys are

    exempt from request signing: `X-Signature` and `X-Timestamp` are not
    required for writes

    in the sandbox (so the developer-portal "Try it" playground works end to
    end).

    `Idempotency-Key` is still required on writes in both environments.


    ## Error format

    All errors follow the `Error` schema with a stable i18n code, an English
    message, and the

    `request_id` for correlation with Safariat logs.


    ## Versioning

    URL-based versioning (`/v1`). No breaking changes within a version. Removals
    are announced

    6 months in advance via the `Sunset` header (RFC 8594).


    ## Rate limiting

    Default limit of 600 req/min per key, contractually adjustable. The
    `X-RateLimit-Limit`,

    `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers are returned on
    every response.

    Exceeding the limit → `429 Too Many Requests` plus the `Retry-After` header.


    ## Sandbox

    An `sk_test_*` key targets the same infrastructure as production but
    isolates bookings in

    `test_mode=true`: no settlement is issued, webhooks carry `test: true`, and
    operators are

    never notified. Traveller emails are still delivered, to the address
    supplied in the request

    and with a `[TEST]` subject prefix, so that the traveller journey can be
    validated end to end

    before going live.
  contact:
    name: SafarAPI Team
    email: api@safariat.ma
    url: https://developers.safariat.ma
  license:
    name: Proprietary — use subject to a signed partner agreement
  termsOfService: https://safariat.ma/legal/partner-terms
servers:
  - url: https://api.safarapi.com/api/partner/v1
    description: >-
      Production and sandbox share this host. The environment is selected by the
      API key prefix: sk_live_ targets production, sk_test_ targets the sandbox.
security:
  - apiKey: []
tags:
  - name: Meta
    description: Metadata for the current key, service health.
  - name: Catalog
    description: Browsing the Safariat adventure catalog.
  - name: Quotes
    description: Price-locked quotes (30 min TTL) ahead of booking.
  - name: Bookings
    description: Creating, retrieving, and cancelling bookings.
  - name: Settlements
    description: Monthly Safariat → partner payout invoices.
  - name: Webhooks
    description: Managing endpoints that receive Safariat events.
  - name: Team
    description: Members of the partner console workspace and their roles.
  - name: Reviews
    description: Read-only access to traveler reviews on the Safariat catalog.
  - name: Files
    description: Presigned uploads for partner-supplied files (currently review photos).
paths:
  /quotes/{quote_id}:
    get:
      tags:
        - Quotes
      summary: Retrieve a quote by id
      operationId: getQuote
      parameters:
        - in: path
          name: quote_id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: The quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
components:
  schemas:
    Quote:
      type: object
      required:
        - id
        - expires_at
        - pricing
        - breakdown
      properties:
        id:
          type: string
          format: uuid
        adventure_slug:
          type: string
        rate_pack_id:
          type: string
          format: uuid
        start_date:
          type: string
          format: date
        start_time:
          type: string
          nullable: true
        rooms:
          type: array
          items:
            $ref: '#/components/schemas/RoomComposition'
        pricing:
          $ref: '#/components/schemas/PartnerPricing'
          description: >
            Revenue breakdown (locked price; `indicative = false`,
            `sandbox_surcharge = 0`).

            `pricing.amount_due_to_safariat` is the amount-match anchor for
            booking creation.
        breakdown:
          $ref: '#/components/schemas/QuoteBreakdown'
        expires_at:
          type: string
          format: date-time
          description: 30-minute TTL after creation.
    RoomComposition:
      type: object
      description: >-
        One entry per room booked: its traveller composition (adults + children
        with ages) and, when the rate pack exposes `room_types`, the room type
        booked. The room supplement is priced from these entries, so two adults
        in one room and two adults in two rooms no longer quote the same amount.
      required:
        - adults
      properties:
        adults:
          type: integer
          minimum: 1
          maximum: 6
        children:
          type: array
          maxItems: 6
          items:
            type: object
            required:
              - age
            properties:
              age:
                type: integer
                minimum: 0
                maximum: 17
        room_type:
          type: string
          maxLength: 100
          nullable: true
          description: >
            Name of the booked room type, taken from `room_types[].name` on the
            rate pack.

            Required when the rate pack exposes room types
            (`partner.quote.roomtype.required` /

            `partner.quote.roomtype.not.found` otherwise), ignored when it
            exposes none.
          example: Double
    PartnerPricing:
      type: object
      description: >
        Revenue breakdown for the AWB channel, in MAD. Canonical home for all
        partner-facing amounts.

        On catalog (search / adventure detail) and quote responses `indicative`
        is `true` and

        `sandbox_surcharge` is `0` (the bank sets its surcharge at booking
        time), so `customer_price`

        equals `b2c_price`. On booking responses `indicative` is `false` and the
        values reflect the

        amounts actually recorded.

        Invariant: `amount_due_to_safariat + awb_commission_share +
        sandbox_surcharge = customer_price`,

        equivalently `amount_due_to_safariat + partner_revenue =
        customer_price`.
      required:
        - b2c_price
        - customer_price
        - amount_due_to_safariat
        - awb_commission_share
        - sandbox_surcharge
        - partner_revenue
        - indicative
      properties:
        b2c_price:
          $ref: '#/components/schemas/Money'
          description: Public B2C reference price (price floor guaranteed to the customer).
        customer_price:
          $ref: '#/components/schemas/Money'
          description: >-
            Total price paid by the customer = `b2c_price + sandbox_surcharge`.
            Equals `b2c_price` before booking.
        amount_due_to_safariat:
          $ref: '#/components/schemas/Money'
          description: >-
            Net reversed to Safariat = operator share + 65% of the base
            commission (the daily wire amount).
        awb_commission_share:
          $ref: '#/components/schemas/Money'
          description: AWB's 35% share of the base commission.
        sandbox_surcharge:
          $ref: '#/components/schemas/Money'
          description: >-
            AWB's own surcharge (100% AWB), derived as `customer_price −
            b2c_price`. `0` before booking.
        partner_revenue:
          $ref: '#/components/schemas/Money'
          description: >
            Total kept by the partner on this booking = `awb_commission_share +
            sandbox_surcharge`.

            Provided so reporting never has to add the two components
            client-side.
        indicative:
          type: boolean
          description: >
            `true` for catalog/quote "from" prices (final amounts confirmed at
            booking);

            `false` on booking responses.
    QuoteBreakdown:
      type: array
      description: >
        Priced lines making up the quote. `travelers` is always present;
        `room_supplement` appears

        when the booked room types carry a supplement. The lines sum to the
        gross price found in

        `pricing`.
      items:
        type: object
        required:
          - label
          - quantity
          - unit_amount
          - subtotal
        properties:
          label:
            type: string
            enum:
              - travelers
              - room_supplement
          quantity:
            type: integer
            description: Traveller count for `travelers`, room count for `room_supplement`.
          unit_amount:
            $ref: '#/components/schemas/Money'
          subtotal:
            $ref: '#/components/schemas/Money'
    Error:
      type: object
      required:
        - code
        - message
        - request_id
      properties:
        code:
          type: string
          description: |
            Stable, documented i18n code. Form `domain.subdomain.detail`
            (e.g. `payment.amount.mismatch`, `auth.api_key.invalid`).
          example: validation.required.field
        message:
          type: string
          description: >-
            English message (for bank logs). API errors are never localized on
            the Safariat side.
          example: Field 'rate_pack_id' is required.
        request_id:
          type: string
          description: Unique request ID on the Safariat side.
        details:
          type: object
          description: Optional structured details (offending field, expected value, etc.).
          additionalProperties: true
    Money:
      type: object
      required:
        - amount
        - currency
      properties:
        amount:
          type: string
          pattern: ^-?\d+\.\d{2}$
          description: Amount as a decimal string with 2 decimals to preserve precision.
          example: '3200.00'
        currency:
          type: string
          enum:
            - MAD
          description: Currency — MAD only in V1.
          example: MAD
  responses:
    Unauthorized:
      description: API key missing, invalid, expired, or incorrect HMAC signature.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing:
              value:
                code: auth.api_key.missing
                message: Authorization header is required.
                request_id: 01HXY2ZRPM3R8K9PSBTRQYNFHC
            invalid:
              value:
                code: auth.api_key.invalid
                message: The provided API key is invalid or has been revoked.
                request_id: 01HXY2ZRPM3R8K9PSBTRQYNFHC
            signatureInvalid:
              value:
                code: auth.signature.invalid
                message: HMAC signature does not match.
                request_id: 01HXY2ZRPM3R8K9PSBTRQYNFHC
            timestampSkew:
              value:
                code: auth.timestamp.skew
                message: Timestamp is more than 5 minutes off server time.
                request_id: 01HXY2ZRPM3R8K9PSBTRQYNFHC
    NotFound:
      description: Resource does not exist or is inaccessible for this partner.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooManyRequests:
      description: Rate limit exceeded for the current key.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: rate_limit.exceeded
            message: Rate limit exceeded for this API key.
            request_id: 01HXY2ZRPM3R8K9PSBTRQYNFHC
  headers:
    RetryAfter:
      schema:
        type: integer
      description: Seconds to wait before the next attempt (RFC 7231).
    XRateLimitLimit:
      schema:
        type: integer
      description: Request limit per minute for the current key.
    XRateLimitRemaining:
      schema:
        type: integer
      description: Requests remaining in the current window.
    XRateLimitReset:
      schema:
        type: integer
        format: int64
      description: Unix timestamp (s) at which the window resets.
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: sk_live_<prefix>_<secret> or sk_test_<prefix>_<secret>
      x-default: sk_test_DEMO0000_replace_with_your_sandbox_key
      description: >
        API key authentication. Issued by the Safariat admin or via the partner
        portal.

        The secret is shown only once at generation.


        Production `sk_live_*` keys must additionally sign every write request

        (`POST`/`PUT`/`DELETE`) with the `X-Timestamp` and `X-Signature`
        headers.

        Sandbox `sk_test_*` keys are exempt from request signing: `X-Signature`
        and

        `X-Timestamp` are not required for writes in the sandbox (so the
        developer-portal

        "Try it" playground works end to end). The `Idempotency-Key` header
        remains

        required on writes in both environments.

````