openapi: 3.0.3
info:
  title: Customer Information System API
  version: 0.2.0
  description: Tenant-scoped CIS compatibility API for banking product integrations.
  x-boundary: CIS owns party/customer reference data, identifiers, contact data, KYC/CDD metadata, relationships, audit, and outbox events. BDS owns branch workflow decisions and consumes CIS by reference.
servers:
  - url: http://localhost:8221
  - url: https://customer-information-system.home.arpa
paths:
  /health:
    get:
      summary: Liveness check
      responses:
        "200":
          description: Service is alive.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
  /ready:
    get:
      summary: Readiness check
      responses:
        "200":
          description: Store dependency is ready.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadyResponse"
        "503":
          $ref: "#/components/responses/Error"
  /v1/openapi.yaml:
    get:
      summary: YAML API contract
      responses:
        "200":
          description: OpenAPI contract.
          content:
            application/yaml:
              schema:
                type: string
  /v1/openapi.json:
    get:
      summary: Backward-compatible API contract alias
      deprecated: true
      responses:
        "200":
          description: YAML OpenAPI contract served for backward compatibility.
          content:
            application/yaml:
              schema:
                type: string
  /v1/customers/{identifierType}/{identifierValue}:
    get:
      summary: Compatibility customer lookup
      security:
        - ServiceToken: []
        - BearerToken: []
      parameters:
        - $ref: "#/components/parameters/TenantID"
        - $ref: "#/components/parameters/ResponseProfile"
        - name: identifierType
          in: path
          required: true
          schema:
            type: string
            enum: [customerId, cif, nationalId]
        - name: identifierValue
          in: path
          required: true
          schema:
            type: string
            maxLength: 128
      responses:
        "200":
          description: Customer found. Sensitive identifiers are masked unless `X-CIS-Response-Profile` is `full`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
  /v1/customers/search:
    get:
      summary: Compatibility customer search
      security:
        - ServiceToken: []
        - BearerToken: []
      parameters:
        - $ref: "#/components/parameters/TenantID"
        - $ref: "#/components/parameters/ResponseProfile"
        - name: q
          in: query
          schema:
            type: string
            maxLength: 128
        - name: limit
          in: query
          description: Maximum number of customers to return. Values over 100 are capped.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        "200":
          description: Matching customers.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Customer"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
  /v1/parties:
    post:
      summary: Create party master-data record
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PartyInput" }
      responses:
        "201": { description: Party created. }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
  /v1/parties/search:
    get:
      summary: Search parties
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters:
        - $ref: "#/components/parameters/TenantID"
        - { name: q, in: query, schema: { type: string, maxLength: 128 } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
      responses:
        "200": { description: Matching parties. }
  /v1/parties/{partyId}:
    get:
      summary: Get party
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters:
        - $ref: "#/components/parameters/TenantID"
        - { name: partyId, in: path, required: true, schema: { type: string } }
      responses:
        "200": { description: Party found. }
        "404": { $ref: "#/components/responses/Error" }
    patch:
      summary: Patch party
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters:
        - $ref: "#/components/parameters/TenantID"
        - { name: partyId, in: path, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PartyPatchInput" }
      responses:
        "200": { description: Party updated. }
  /v1/parties/{partyId}/activate:
    post:
      summary: Activate party lifecycle
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Party activated. } }
  /v1/parties/{partyId}/restrict:
    post:
      summary: Restrict party lifecycle
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Party restricted. } }
  /v1/parties/{partyId}/close:
    post:
      summary: Close party lifecycle
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Party closed. } }
  /v1/customer-files:
    post:
      summary: Create customer information file
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustomerFileInput" }
      responses: { "201": { description: Customer file created. } }
  /v1/customer-files/{cif}:
    get:
      summary: Get customer information file by CIF
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: cif, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Customer file found. }, "404": { $ref: "#/components/responses/Error" } }
  /v1/customer-files/export:
    get:
      summary: Export customer-file view for regulatory or operational extracts
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: q, in: query, schema: { type: string } }, { name: limit, in: query, schema: { type: integer, maximum: 100 } }]
      responses: { "200": { description: Customer-file export rows. } }
  /v1/parties/{partyId}/identifiers:
    post:
      summary: Add party identifier
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: Identifier created. } }
    get:
      summary: List party identifiers
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Identifiers. } }
  /v1/parties/{partyId}/addresses:
    post:
      summary: Add structured party address
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: Address created. } }
    get:
      summary: List party addresses
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Addresses. } }
  /v1/parties/{partyId}/contact-points:
    post:
      summary: Add party contact point
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: Contact point created. } }
    get:
      summary: List party contact points
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Contact points. } }
  /v1/parties/{partyId}/kyc-cases:
    post:
      summary: Open KYC/CDD case
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: KYC case opened. } }
    get:
      summary: List party KYC/CDD cases
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: KYC cases. } }
  /v1/kyc-cases/{caseId}/checks:
    post:
      summary: Add KYC check result
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: caseId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: KYC check added. } }
  /v1/kyc-cases/{caseId}/evidence:
    post:
      summary: Add KYC evidence metadata
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: caseId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: Evidence metadata added. } }
  /v1/kyc-cases/{caseId}/complete:
    post:
      summary: Complete KYC/CDD case
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: caseId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: KYC case completed. } }
  /v1/parties/{partyId}/risk-profile:
    get:
      summary: Get party risk profile
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Risk profile. } }
    put:
      summary: Upsert party risk profile
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Risk profile updated. } }
  /v1/parties/{partyId}/relationships:
    post:
      summary: Create party relationship
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "201": { description: Relationship created. } }
    get:
      summary: List party relationships
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Relationships. } }
  /v1/relationships/{relationshipId}:
    patch:
      summary: Patch relationship metadata
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: relationshipId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Relationship updated. } }
  /v1/parties/{partyId}/audit-events:
    get:
      summary: List party audit events
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: partyId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Audit events. } }
  /v1/events/outbox:
    get:
      summary: List CIS domain events
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }]
      responses: { "200": { description: Outbox events. } }
  /v1/events/outbox/{eventId}/ack:
    post:
      summary: Acknowledge CIS domain event
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }, { name: eventId, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Event acknowledged. } }
  /metrics:
    get:
      summary: Prometheus-compatible metrics
      security: [{ServiceToken: []}, {BearerToken: []}]
      parameters: [{ $ref: "#/components/parameters/TenantID" }]
      responses: { "200": { description: Text metrics. } }
components:
  securitySchemes:
    ServiceToken:
      type: apiKey
      in: header
      name: X-Service-Token
    BearerToken:
      type: http
      scheme: bearer
  parameters:
    TenantID:
      name: X-Tenant-ID
      in: header
      required: true
      schema:
        type: string
        maxLength: 80
    ResponseProfile:
      name: X-CIS-Response-Profile
      in: header
      required: false
      description: Set to `full` only for callers allowed to receive unmasked DOB and national ID values.
      schema:
        type: string
        enum: [masked, full]
        default: masked
  responses:
    Error:
      description: JSON error response.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
  schemas:
    HealthResponse:
      type: object
      required: [status]
      properties:
        status:
          type: string
          example: ok
    ReadyResponse:
      type: object
      required: [status, customers]
      properties:
        status:
          type: string
          example: ready
        customers:
          type: integer
          minimum: 0
    ErrorResponse:
      type: object
      required: [error, message]
      properties:
        error:
          type: string
        message:
          type: string
        requestId:
          type: string
    Customer:
      type: object
      required: [customerId, cif, fullName, customerType, segment, riskRating, kycLevel, status]
      properties:
        customerId:
          type: string
        cif:
          type: string
        fullName:
          type: string
        dateOfBirth:
          type: string
          format: date
          description: Present only for `full` response profile.
        nationalId:
          type: string
          description: Masked by default; full value requires `X-CIS-Response-Profile: full`.
        phone:
          type: string
        email:
          type: string
          format: email
        nationality:
          type: string
        customerType:
          type: string
        segment:
          type: string
        riskRating:
          type: string
        kycLevel:
          type: string
        status:
          type: string
    PartyInput:
      type: object
      required: [partyType, displayName, legalName]
      properties:
        partyType: { type: string, enum: [individual, organization, sole_proprietor] }
        displayName: { type: string }
        legalName: { type: string }
        dateOfBirth: { type: string, format: date }
        nationality: { type: string }
    PartyPatchInput:
      type: object
      properties:
        displayName: { type: string }
        legalName: { type: string }
        dateOfBirth: { type: string, format: date }
        nationality: { type: string }
        statusReason: { type: string }
    CustomerFileInput:
      type: object
      required: [partyId]
      properties:
        partyId: { type: string }
        cif: { type: string }
        phone: { type: string }
        email: { type: string }
        customerType: { type: string }
        segment: { type: string }
        riskRating: { type: string }
        kycLevel: { type: string }
