{
  "openapi": "3.0.3",
  "info": {
    "title": "Client subscription API",
    "version": "1.0.0",
    "description": "Organization-scoped integration subscriptions. New API subscriptions are queued for initial background screening immediately after creation; the 201 response does not wait for screening or delivery. Active subscriptions are also screened after full import jobs or admin-triggered checks; name/alias, reordered name-part, conservative spelling-variation, or linked-entity matches are candidates for review. Configured webhooks receive initial screening results and subsequent changes with a stable eventId. Delivery is at least once; receivers must deduplicate event IDs. Creation is idempotent per organization, integration key ID, and Idempotency-Key. Omitted/null webhook uses the organization default; no default still permits creation. Same normalized body returns the original 201, including cancelled records; changed body returns 409. Names are trimmed; missing/null aliases equal an empty list. Other fields and alias order are significant. Maximum body: 64 KiB. Rate limit: 120 requests/minute/organization/application process."
  },
  "servers": [
    {
      "url": "{apiBaseUrl}",
      "variables": {
        "apiBaseUrl": {
          "default": "https://api.example.com",
          "description": "Replace with your deployed API base URL."
        }
      }
    }
  ],
  "security": [
    {
      "IntegrationKey": []
    }
  ],
  "paths": {
    "/api/v1/subscriptions": {
      "post": {
        "operationId": "createClientSubscription",
        "summary": "Create or replay a subscription",
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[!-~]+$"
            },
            "description": "Stable, case-sensitive creation request ID. Reuse across transport retries."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientSubscriptionRequest"
              },
              "example": {
                "name": "Example Trading Ltd",
                "country": "GB",
                "registrationNumber": "12345678",
                "aliases": [
                  "Example Trading",
                  "Example Logistics"
                ],
                "externalReference": "company-456",
                "webhookChannelId": "64f1b7c4a113c3b2e8f0aa12"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created or replayed with its latest lifecycle and screening state. New records start pending and are durably queued for background screening against stored sanctions data.",
            "headers": {
              "Location": {
                "description": "Subscription retrieval URL.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientSubscriptionResponse"
                },
                "example": {
                  "subscriptionId": "64f1b7c4a113c3b2e8f0aa34",
                  "name": "Example Trading Ltd",
                  "country": "GB",
                  "registrationNumber": "12345678",
                  "aliases": [
                    "Example Trading",
                    "Example Logistics"
                  ],
                  "externalReference": "company-456",
                  "webhookChannelId": "64f1b7c4a113c3b2e8f0aa12",
                  "screeningStatus": "pending",
                  "deliveryStatus": "not_started",
                  "createdAt": "2026-10-11T10:00:00Z",
                  "deletedAt": null,
                  "matches": [],
                  "screenedAt": null,
                  "lastScreeningJobId": null,
                  "screeningError": null
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, idempotency header, or channel ownership.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ApiError"
                    },
                    {
                      "$ref": "#/components/schemas/ValidationProblem"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing integration key; response body may be empty."
          },
          "409": {
            "description": "Idempotency payload conflict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "413": {
            "description": "Request body exceeds 64 KiB."
          },
          "429": {
            "description": "Organization request limit exceeded.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscriptions/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Subscription identifier. Invalid identifiers return 404."
        }
      ],
      "get": {
        "operationId": "getClientSubscription",
        "summary": "Get an organization-owned subscription",
        "responses": {
          "200": {
            "description": "Stored subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientSubscriptionResponse"
                },
                "example": {
                  "subscriptionId": "64f1b7c4a113c3b2e8f0aa34",
                  "name": "Example Trading Ltd",
                  "country": "GB",
                  "registrationNumber": "12345678",
                  "aliases": [
                    "Example Trading",
                    "Example Logistics"
                  ],
                  "externalReference": "company-456",
                  "webhookChannelId": "64f1b7c4a113c3b2e8f0aa12",
                  "screeningStatus": "pending",
                  "deliveryStatus": "not_started",
                  "createdAt": "2026-10-11T10:00:00Z",
                  "deletedAt": null,
                  "matches": [],
                  "screenedAt": null,
                  "lastScreeningJobId": null,
                  "screeningError": null
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing integration key; response body may be empty."
          },
          "404": {
            "description": "Unknown, invalid, or foreign subscription ID."
          },
          "429": {
            "description": "Organization request limit exceeded.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "cancelClientSubscription",
        "summary": "Cancel without losing idempotency identity",
        "responses": {
          "204": {
            "description": "Subscription cancelled, including repeat cancellation. No response body."
          },
          "401": {
            "description": "Invalid or missing integration key; response body may be empty."
          },
          "404": {
            "description": "Unknown, invalid, or foreign subscription ID."
          },
          "429": {
            "description": "Organization request limit exceeded.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "IntegrationKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Api-Key",
        "description": "Use an integration key provisioned by an authenticated organization member. User JWTs do not authenticate these endpoints."
      }
    },
    "schemas": {
      "ClientSubscriptionRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300,
            "description": "Entity name. Non-blank, up to 300 characters; surrounding whitespace is trimmed."
          },
          "country": {
            "type": "string",
            "nullable": true,
            "maxLength": 100,
            "description": "Country identifier or name, up to 100 characters. No fixed country-code format is enforced."
          },
          "registrationNumber": {
            "type": "string",
            "nullable": true,
            "maxLength": 100,
            "description": "Entity registration number, up to 100 characters."
          },
          "aliases": {
            "type": "array",
            "nullable": true,
            "maxItems": 20,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 300
            },
            "description": "Up to 20 non-blank aliases, at most 300 characters each. Omitted or null becomes an empty list."
          },
          "externalReference": {
            "type": "string",
            "nullable": true,
            "maxLength": 200,
            "description": "Your own entity ID, up to 200 characters. Returned for correlation; does not enforce uniqueness."
          },
          "webhookChannelId": {
            "type": "string",
            "nullable": true,
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "An existing webhook ID from your organization. Omitted or null uses the organization default; no default still permits creation."
          }
        }
      },
      "ClientSubscriptionResponse": {
        "type": "object",
        "required": [
          "subscriptionId",
          "name",
          "country",
          "registrationNumber",
          "aliases",
          "externalReference",
          "webhookChannelId",
          "screeningStatus",
          "deliveryStatus",
          "createdAt",
          "deletedAt",
          "matches",
          "screenedAt",
          "lastScreeningJobId",
          "screeningError"
        ],
        "properties": {
          "subscriptionId": {
            "type": "string",
            "description": "Subscription identifier for retrieval and cancellation."
          },
          "name": {
            "type": "string",
            "description": "Stored entity name, trimmed on creation."
          },
          "country": {
            "type": "string",
            "nullable": true,
            "description": "Country provided in the request."
          },
          "registrationNumber": {
            "type": "string",
            "nullable": true,
            "description": "Registration number provided in the request."
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Stored aliases; an empty list when none were provided."
          },
          "externalReference": {
            "type": "string",
            "nullable": true,
            "description": "Your entity reference, returned unchanged."
          },
          "webhookChannelId": {
            "type": "string",
            "nullable": true,
            "description": "Effective channel captured at creation, including an organization default."
          },
          "screeningStatus": {
            "type": "string",
            "description": "Pending while initial screening is queued; matched/no_match/failed afterwards, or cancelled after deletion. Full-import/admin checks also refresh results.",
            "enum": [
              "pending",
              "matched",
              "no_match",
              "failed",
              "cancelled"
            ]
          },
          "deliveryStatus": {
            "type": "string",
            "description": "Notification delivery state; pending includes delayed retries. Webhook receivers should deduplicate using the stable eventId.",
            "enum": [
              "not_started",
              "not_configured",
              "pending",
              "delivered",
              "failed",
              "cancelled"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC creation timestamp. Retries preserve the original value."
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "UTC cancellation timestamp, or null for an active subscription."
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SanctionsMatch"
            }
          },
          "screenedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastScreeningJobId": {
            "type": "string",
            "nullable": true
          },
          "screeningError": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ApiError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "ValidationProblem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "SanctionsMatch": {
        "type": "object",
        "required": [
          "sanctionsEntryId",
          "source",
          "externalId",
          "name",
          "country",
          "matchKind",
          "score",
          "matchedName",
          "matchReason"
        ],
        "properties": {
          "sanctionsEntryId": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "matchKind": {
            "type": "string",
            "enum": [
              "name_or_alias",
              "linked_entity",
              "name_tokens",
              "fuzzy_name_tokens"
            ]
          },
          "score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Matching similarity, not an identity probability."
          },
          "matchedName": {
            "type": "string",
            "nullable": true
          },
          "matchReason": {
            "type": "string",
            "nullable": true
          }
        }
      }
    }
  }
}
