{
  "openapi": "3.1.0",
  "info": {
    "title": "Webhook Subscription API",
    "version": "v3"
  },
  "servers": [
    {
      "url": "https://api-ext-sboxmeta.partners.spotnana.com",
      "description": "Sandbox URL"
    }
  ],
  "security": [
    {
      "Bearer": []
    }
  ],
  "components": {
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Forbidden",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "The specified resource was not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Unauthorized",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "CreateWebhookSubscriptionRequest": {
        "type": "object",
        "required": [
          "subscriberId",
          "eventSource",
          "name",
          "eventTypes",
          "url"
        ],
        "properties": {
          "subscriberId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the company that owns this webhook subscription.\n"
          },
          "eventSource": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EventSource"
              }
            ],
            "description": "Defines which event sources this subscription should match. Replaces the previous\nsingle `eventSourceId` field with scoped, typed matching.\n"
          },
          "name": {
            "type": "string",
            "description": "Human-readable display name for the subscription. Must be non-blank."
          },
          "eventTypes": {
            "type": "array",
            "description": "List of event types this subscription should receive. At least one is required.",
            "items": {
              "$ref": "#/components/schemas/WebhookEventTypeEnum"
            },
            "minItems": 1
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint that Spotnana will POST event payloads to. Must use the `https`\nscheme and accept `POST` requests.\n"
          },
          "isActive": {
            "type": "boolean",
            "default": true,
            "description": "Whether the subscription is active. When `false`, the subscription is preserved but\nincoming events will not be matched against it and no deliveries will be made.\n"
          },
          "customHeaders": {
            "type": "object",
            "description": "Additional HTTP headers to include on every webhook request sent to `url`. Provided\nas a map of header name to header value. Reserved headers managed by Spotnana\n(such as `Authorization` and `Content-Type`) will override any values supplied here.\n",
            "additionalProperties": {
              "type": "string",
              "x-additionalPropertiesName": "headerName"
            },
            "example": {
              "X-Custom-Header": "my-value",
              "Authorization-Extra": "token123"
            }
          }
        }
      },
      "CreateWebhookSubscriptionResponse": {
        "type": "object",
        "required": [
          "id",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the newly created webhook subscription."
          },
          "hmacSigningSecret": {
            "type": "string",
            "description": "Secret used to verify the HMAC-SHA256 signature on incoming webhook requests.\nReturned only once, at subscription creation, and cannot be retrieved later. Store\nit securely.\n"
          },
          "createdAt": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DateModel"
              }
            ],
            "description": "Timestamp at which the subscription was created."
          }
        }
      },
      "DateModel": {
        "title": "Date",
        "description": "Date in ISO 8601 standard.",
        "type": "object",
        "required": [
          "iso8601"
        ],
        "properties": {
          "iso8601": {
            "type": "string",
            "pattern": "^\\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$",
            "example": "2017-07-21"
          }
        }
      },
      "ErrorParameter": {
        "type": "object",
        "title": "ErrorParameter",
        "description": "Error parameter",
        "properties": {
          "name": {
            "type": "string",
            "description": "Parameter name"
          },
          "value": {
            "type": "string",
            "description": "Parameter value"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "debugIdentifier": {
            "type": "string",
            "description": "Link to debug the error internally."
          },
          "errorMessages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "errorCode": {
                  "type": "string",
                  "description": "Error code to identify the specific errors."
                },
                "message": {
                  "type": "string",
                  "description": "Message containing details of error."
                },
                "errorParameters": {
                  "type": "array",
                  "description": "Error message parameters.",
                  "items": {
                    "$ref": "#/components/schemas/ErrorParameter"
                  }
                },
                "errorDetail": {
                  "type": "string",
                  "description": "More details about the error."
                }
              }
            }
          }
        }
      },
      "EventSource": {
        "type": "object",
        "description": "Defines which event sources a subscription should match. Contains a list of scopes\nthat are OR'd together — an event matches if it satisfies ANY scope.\n",
        "required": [
          "scopes"
        ],
        "properties": {
          "scopes": {
            "type": "array",
            "description": "List of scopes evaluated with OR logic. An event matches if at least one scope\nis satisfied.\n",
            "items": {
              "$ref": "#/components/schemas/EventSourceScope"
            },
            "minItems": 1
          }
        }
      },
      "EventSourcePredicate": {
        "type": "object",
        "description": "A single predicate that constrains which event sources match. The event's value\nfor the given `type` is evaluated against `values` using the `comparator`.\n",
        "required": [
          "type",
          "values"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/EventSourceScopeType"
          },
          "values": {
            "type": "array",
            "description": "List of entity IDs to match against.",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "minItems": 1
          },
          "comparator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EventSourceScopeComparator"
              }
            ],
            "description": "Comparator for evaluation. Defaults to `IN`."
          },
          "valuesWithLabel": {
            "type": "array",
            "description": "Server-resolved labels for each entry in `values`. Read-only, returned only\nin responses.\n",
            "items": {
              "$ref": "#/components/schemas/EventSourceValueWithLabel"
            },
            "readOnly": true
          }
        }
      },
      "EventSourceScope": {
        "type": "object",
        "description": "A single scope consisting of one or more predicates. All predicates within a scope\nare AND'd together — the scope matches only when every predicate is satisfied.\n",
        "required": [
          "predicates"
        ],
        "properties": {
          "predicates": {
            "type": "array",
            "description": "List of predicates that must all be satisfied for this scope to match.",
            "items": {
              "$ref": "#/components/schemas/EventSourcePredicate"
            },
            "minItems": 1
          }
        }
      },
      "EventSourceScopeComparator": {
        "type": "string",
        "description": "Comparator used to evaluate the predicate.",
        "default": "IN",
        "enum": [
          "IN"
        ]
      },
      "EventSourceScopeType": {
        "type": "string",
        "description": "The type of event source entity.",
        "enum": [
          "COMPANY",
          "BOOKING_TMC",
          "CONTRACTING_TMC"
        ]
      },
      "EventSourceValueWithLabel": {
        "type": "object",
        "description": "An event source entity ID paired with its resolved display name.",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the entity."
          },
          "name": {
            "type": "string",
            "description": "Display name of the entity."
          }
        }
      },
      "ListWebhookSubscriptionsResponse": {
        "type": "object",
        "required": [
          "subscriptions",
          "totalCount"
        ],
        "properties": {
          "subscriptions": {
            "type": "array",
            "description": "Page of webhook subscriptions matching the request.",
            "items": {
              "$ref": "#/components/schemas/WebhookSubscription"
            }
          },
          "totalCount": {
            "type": "integer",
            "description": "Total number of subscriptions for the subscriber, across all pages."
          }
        }
      },
      "UpdateWebhookSubscriptionRequest": {
        "type": "object",
        "description": "Partial update for a webhook subscription. Only fields included in the request body are\nmodified; omitted fields are left unchanged.\n",
        "properties": {
          "eventSource": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EventSource"
              }
            ],
            "description": "Replacement event source scope. If provided, fully replaces the existing scope.\n"
          },
          "name": {
            "type": "string",
            "description": "Human-readable display name for the subscription."
          },
          "eventTypes": {
            "type": "array",
            "description": "Replacement list of event types this subscription should receive. If provided, must\ncontain at least one entry; the new list fully replaces the existing one.\n",
            "items": {
              "$ref": "#/components/schemas/WebhookEventTypeEnum"
            },
            "minItems": 1
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the subscription is active. When `false`, the subscription is preserved but\nincoming events will not be matched against it and no deliveries will be made.\n"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint that Spotnana will POST event payloads to. Must use the `https`\nscheme and accept `POST` requests.\n"
          },
          "customHeaders": {
            "type": "object",
            "description": "Replacement set of additional HTTP headers to include on every webhook request.\nSending an empty map clears all existing custom headers; omitting the field leaves\nthem unchanged. Reserved headers managed by Spotnana (such as `Authorization` and\n`Content-Type`) will override any values supplied here.\n",
            "additionalProperties": {
              "type": "string",
              "x-additionalPropertiesName": "headerName",
              "example": {
                "X-Custom-Header": "my-value",
                "Authorization-Extra": "token123"
              }
            }
          }
        }
      },
      "WebhookAuthTypeEnum": {
        "type": "string",
        "default": "HMAC_SHA256",
        "enum": [
          "OAUTH2",
          "HMAC_SHA256"
        ]
      },
      "WebhookEventTypeEnum": {
        "type": "string",
        "description": "Type of event the webhook subscription will receive.",
        "enum": [
          "TRAVELER_V2",
          "PNR_V3",
          "TRIP_DETAILS_V3",
          "PNR_APPROVAL",
          "AGENT_TASK_DETAILS",
          "SERVICE_CHARGE"
        ]
      },
      "WebhookSubscription": {
        "type": "object",
        "required": [
          "id",
          "subscriberId",
          "eventSource",
          "name",
          "eventTypes",
          "url",
          "authType",
          "isActive"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the webhook subscription."
          },
          "name": {
            "type": "string",
            "description": "Human-readable display name for the subscription."
          },
          "eventTypes": {
            "type": "array",
            "description": "List of event types this subscription is configured to receive.",
            "items": {
              "$ref": "#/components/schemas/WebhookEventTypeEnum"
            }
          },
          "subscriberId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the company that owns this webhook subscription."
          },
          "eventSource": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EventSource"
              }
            ],
            "description": "Defines which event sources this subscription matches.\n"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint that Spotnana POSTs event payloads to."
          },
          "customHeaders": {
            "type": "object",
            "description": "Additional HTTP headers included on every webhook request sent to `url`. Reserved\nheaders managed by Spotnana (such as `Authorization` and `Content-Type`) take\nprecedence over values configured here.\n",
            "additionalProperties": {
              "type": "string",
              "x-additionalPropertiesName": "headerName"
            },
            "example": {
              "X-Custom-Header": "my-value",
              "Authorization-Extra": "token123"
            }
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the subscription is active. When `false`, the subscription is preserved but\nincoming events will not be matched against it and no deliveries will be made.\n"
          },
          "createdBy": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the user who created the subscription."
          },
          "updatedBy": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the user who last updated the subscription."
          },
          "createdAt": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DateModel"
              }
            ],
            "description": "Timestamp at which the subscription was created."
          },
          "updatedAt": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DateModel"
              }
            ],
            "description": "Timestamp at which the subscription was last updated."
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Webhook Subscription",
      "description": "APIs to create, list, retrieve, and update webhook subscriptions."
    }
  ],
  "paths": {
    "/v3/webhooks/subscriptions": {
      "post": {
        "tags": [
          "Webhook Subscription"
        ],
        "summary": "Create Webhook Subscription",
        "operationId": "createWebhookSubscription",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookSubscriptionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Webhook Subscription Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateWebhookSubscriptionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v3/webhooks/subscriptions/subscriber/{subscriberId}/list": {
      "get": {
        "tags": [
          "Webhook Subscription"
        ],
        "summary": "List Webhook Subscriptions",
        "operationId": "listWebhookSubscriptions",
        "parameters": [
          {
            "name": "subscriberId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful List Webhook Subscriptions Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListWebhookSubscriptionsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v3/webhooks/subscriptions/{subscriptionId}": {
      "get": {
        "tags": [
          "Webhook Subscription"
        ],
        "summary": "Get Webhook Subscription",
        "operationId": "getWebhookSubscription",
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Get Webhook Subscription Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhook Subscription"
        ],
        "summary": "Update Webhook Subscription",
        "operationId": "updateWebhookSubscription",
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookSubscriptionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook Subscription Updated Successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  }
}