openapi: 3.1.0 info: title: OffersBeat Enterprise API version: "1.0" summary: Server-to-server API for approved OffersBeat enterprise and white-label partners. description: | The OffersBeat Enterprise API lets an approved partner create anonymous traveler journeys, submit hotel intents, retrieve normalized offers, select an offer and issue a controlled booking handoff. Credentials are bound server-side to one tenant, brand and channel. Never embed the bearer token in browser code. servers: - url: https://offersbeat.com/api/v1/enterprise description: Production security: - bearerAuth: [] tags: - name: Context - name: Journey - name: Offers - name: Handoff paths: /context: get: tags: [Context] summary: Verify authenticated enterprise context operationId: getEnterpriseContext responses: '200': description: Server-resolved tenant, brand, channel and credential scopes. content: application/json: schema: $ref: '#/components/schemas/EnterpriseContextResponse' '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} /anonymous-sessions: post: tags: [Journey] summary: Create an anonymous journey session operationId: createAnonymousSession requestBody: required: false content: application/json: schema: type: object additionalProperties: false responses: '201': description: Anonymous session issued. content: application/json: schema: type: object required: [data] properties: data: type: object required: [type, anonymous_token, expires_at] properties: type: {type: string, const: anonymous_session} anonymous_token: {type: string} expires_at: {type: string, format: date-time} '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} /intents: post: tags: [Journey] summary: Create and route a traveler intent operationId: createIntent parameters: - $ref: '#/components/parameters/AnonymousToken' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/HotelIntentRequest' examples: hotel: value: vertical: travel.hotel request: destination: {country: GR, city: Athens} stay: {check_in: '2026-10-20', check_out: '2026-10-22'} occupancy: {rooms: 1, adults: 2, children: 0, child_ages: []} traveler_preferences: breakfast: true refundable: true requirements: {hard: [], preferred: []} response_rules: {max_offers: 10} expires_at: '2026-10-19T12:00:00+03:00' responses: '202': description: Intent accepted and routing started. headers: Location: schema: {type: string} content: application/json: schema: $ref: '#/components/schemas/IntentAcceptedResponse' '401': {$ref: '#/components/responses/Unauthorized'} '403': {$ref: '#/components/responses/Forbidden'} '409': {$ref: '#/components/responses/Conflict'} '422': {$ref: '#/components/responses/Validation'} /intents/{intentId}: get: tags: [Journey] summary: Read intent lifecycle status operationId: getIntent parameters: - $ref: '#/components/parameters/IntentId' - $ref: '#/components/parameters/AnonymousToken' responses: '200': description: Intent status. content: application/json: schema: type: object additionalProperties: true '401': {$ref: '#/components/responses/Unauthorized'} '404': {$ref: '#/components/responses/NotFound'} /intents/{intentId}/finalize: post: tags: [Offers] summary: Finalize the current offer collection description: Returns 409 while supplier dispatch is still in progress or the intent cannot yet be finalized. operationId: finalizeIntentOffers parameters: - $ref: '#/components/parameters/IntentId' - $ref: '#/components/parameters/AnonymousToken' responses: '200': description: Offer collection finalized. content: application/json: schema: $ref: '#/components/schemas/OfferCollectionResponse' '401': {$ref: '#/components/responses/Unauthorized'} '404': {$ref: '#/components/responses/NotFound'} '409': {$ref: '#/components/responses/Conflict'} /intents/{intentId}/offers: get: tags: [Offers] summary: Read normalized eligible offers operationId: getIntentOffers parameters: - $ref: '#/components/parameters/IntentId' - $ref: '#/components/parameters/AnonymousToken' responses: '200': description: Finalized offer collection. content: application/json: schema: $ref: '#/components/schemas/OfferCollectionResponse' '401': {$ref: '#/components/responses/Unauthorized'} '404': {$ref: '#/components/responses/NotFound'} '409': {$ref: '#/components/responses/Conflict'} /intents/{intentId}/selections: post: tags: [Offers] summary: Select an eligible offer operationId: selectOffer parameters: - $ref: '#/components/parameters/IntentId' - $ref: '#/components/parameters/AnonymousToken' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: type: object required: [offer_id] additionalProperties: false properties: offer_id: {type: string, format: uuid} responses: '201': description: Offer selected. content: application/json: schema: type: object additionalProperties: true '200': description: Idempotent replay of an existing selection. '401': {$ref: '#/components/responses/Unauthorized'} '404': {$ref: '#/components/responses/NotFound'} '409': {$ref: '#/components/responses/Conflict'} '422': {$ref: '#/components/responses/Validation'} /intents/{intentId}/booking-handoff: post: tags: [Handoff] summary: Issue a controlled anonymous booking handoff operationId: createBookingHandoff parameters: - $ref: '#/components/parameters/IntentId' - $ref: '#/components/parameters/AnonymousToken' - $ref: '#/components/parameters/IdempotencyKey' responses: '201': description: Booking handoff issued. content: application/json: schema: type: object required: [data] properties: data: type: object required: [type, id, intent_id, offer_id, mode, status, handoff_token, checkout_available, traveler_pii_received] properties: type: {type: string, const: anonymous_booking_handoff} id: {type: string, format: uuid} intent_id: {type: string, format: uuid} offer_id: {type: string, format: uuid} mode: {type: string} status: {type: string} handoff_token: {type: string} handoff_url: {type: [string, 'null']} checkout_available: {type: boolean} expires_at: {type: [string, 'null'], format: date-time} traveler_pii_received: {type: boolean, const: false} replayed: {type: boolean} '200': description: Idempotent replay of an existing handoff. '401': {$ref: '#/components/responses/Unauthorized'} '404': {$ref: '#/components/responses/NotFound'} '409': {$ref: '#/components/responses/Conflict'} '422': {$ref: '#/components/responses/Validation'} components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: OffersBeat Enterprise Token parameters: IntentId: name: intentId in: path required: true schema: {type: string, format: uuid} AnonymousToken: name: X-OffersBeat-Anonymous-Token in: header required: true schema: {type: string} description: Token returned by POST /anonymous-sessions for the same channel journey. IdempotencyKey: name: Idempotency-Key in: header required: true schema: type: string minLength: 8 maxLength: 191 pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$' schemas: EnterpriseContextResponse: type: object required: [data] properties: data: type: object additionalProperties: true required: [type, client_key, tenant, brand, channel, credential] properties: type: {type: string, const: enterprise_api_context} client_key: {type: string} tenant: {type: object, additionalProperties: true} brand: {type: object, additionalProperties: true} channel: {type: object, additionalProperties: true} credential: {type: object, additionalProperties: true} HotelIntentRequest: type: object required: [vertical, request] additionalProperties: false properties: vertical: type: string pattern: '^[a-z0-9]+(?:[._-][a-z0-9]+)*$' example: travel.hotel request: type: object additionalProperties: true description: Vertical-specific traveler request. Hotel examples use destination, stay and occupancy. traveler_preferences: type: object additionalProperties: false properties: breakfast: {type: boolean} refundable: {type: boolean} non_smoking_room: {type: boolean} parking: {type: boolean} pool: {type: boolean} gym: {type: boolean} spa: {type: boolean} beachfront: {type: boolean} star_rating_min: {type: [number, 'null'], minimum: 1, maximum: 5} bed_type: {type: [string, 'null'], maxLength: 40} accessible_room_required: {type: boolean} pet_friendly_required: {type: boolean} requirements: type: object additionalProperties: true response_rules: type: object additionalProperties: true expires_at: type: [string, 'null'] format: date-time IntentAcceptedResponse: type: object required: [data] properties: data: type: object required: [type, id, vertical, status, routing_target_count, replayed] properties: type: {type: string, const: intent} id: {type: string, format: uuid} vertical: {type: string} status: {type: string} routing_target_count: {type: integer} replayed: {type: boolean} created_at: {type: [string, 'null'], format: date-time} expires_at: {type: [string, 'null'], format: date-time} OfferCollectionResponse: type: object required: [data] properties: data: type: object required: [type, intent_id, intent_status, offers, offer_count] properties: type: {type: string, const: offer_collection} intent_id: {type: string, format: uuid} intent_status: {type: string} offers: type: array items: $ref: '#/components/schemas/Offer' offer_count: {type: integer} suppressed_offer_count: {type: integer} matching_finalized_at: {type: [string, 'null'], format: date-time} Offer: type: object additionalProperties: false properties: type: {type: string, const: offer} id: {type: string, format: uuid} rank: {type: [integer, 'null']} status: {type: string} offer_type: {type: string} product: {type: object, additionalProperties: true} pricing: type: [object, 'null'] additionalProperties: true availability: type: [object, 'null'] additionalProperties: true terms: {type: object, additionalProperties: true} inclusions: {type: object, additionalProperties: true} matching: {type: object, additionalProperties: true} valid_from: {type: [string, 'null'], format: date-time} valid_until: {type: [string, 'null'], format: date-time} selectable: {type: boolean} ErrorResponse: type: object required: [error] properties: error: type: object required: [code, message] properties: code: {type: string} message: {type: string} fields: {type: object, additionalProperties: true} responses: Unauthorized: description: Authentication or anonymous session failed. content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} Forbidden: description: Credential does not have the required scope. content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} NotFound: description: Resource is unavailable in the authenticated channel context. content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} Conflict: description: Lifecycle or idempotency conflict. content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} Validation: description: Validation failed. content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'}