openapi: 3.0.3
info:
  title: Seshnova Partner API
  version: 0.1.5
  description: 'Partner-facing contract for the Seshnova API Gateway REST API.


    The public surface is the API Gateway/Lambda facade exposed to partners.

    All operations are JSON POST endpoints on the deployed API Gateway stage.


    Post-call HTTPS callbacks and Flow Blocks delivery features are outside this

    Gateway contract. Contact Seshnova if you need those integration patterns.


    Environment-specific connection details are issued during partner onboarding (not

    published on the public docs host).'
servers:
- url: https://api.seshnova.com
  description: Partner API base URL. Your environment-specific hostname is provided
    during onboarding.
tags:
- name: Balance
  description: Balance read/write operations.
- name: Usage
  description: Read-only usage and billed call history.
- name: Accounts
  description: Account lifecycle and caller ID management.
- name: Calls
  description: Call setup operations.
security:
- cognitoBearer: []
paths:
  /get_balance:
    post:
      tags:
      - Balance
      operationId: getBalance
      summary: Get account balance
      description: 'Returns the current monetary balance for a 12-digit account code.

        Tenant, product, and currency context are derived from the authenticated

        partner credential and authorizer claims.

        '
      parameters:
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetBalanceRequest'
            examples:
              basic:
                value:
                  account: '123456789012'
                  product_id: prepaid_uk
                  currency: GBP
      responses:
        '200':
          description: Balance found.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetBalanceResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/ServerError'
  /call_history:
    post:
      tags:
      - Usage
      operationId: callHistory
      summary: Recent billed call history for an account
      description: 'Returns up to **10** most recent billed calls for the account
        within the **last 30 days**

        (UTC). Each row exposes **called_number**, **duration_seconds**, and **amount_charged**.

        **Tenant** is always taken from the JWT; **`product_id` and `currency` in
        the JSON body

        take precedence** over Cognito claims when present. Send **`currency`** explicitly
        when

        it is not on the token. Empty history returns **200** with an empty `calls`
        array.

        '
      parameters:
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallHistoryRequest'
            examples:
              basic:
                value:
                  account: '123456789012'
                  product_id: prepaid_uk
                  currency: GBP
      responses:
        '200':
          description: History retrieved (possibly empty).
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallHistoryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/ServerError'
  /topup:
    post:
      tags:
      - Balance
      operationId: topup
      summary: Add monetary balance
      description: 'Adds credit to an existing account. This operation is idempotent
        by

        tenant, operation, and Idempotency-Key.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BalanceMutationRequest'
            examples:
              topup:
                value:
                  account: '123456789012'
                  amount: 10.0
                  product_id: prepaid_uk
                  currency: GBP
      responses:
        '200':
          description: Balance added.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
            X-Transaction-Id:
              $ref: '#/components/headers/TransactionId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BalanceMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '500':
          $ref: '#/components/responses/ServerError'
  /debit_balance:
    post:
      tags:
      - Balance
      operationId: debitBalance
      summary: Debit monetary balance
      description: 'Debits credit from an existing account. This operation is idempotent
        by

        tenant, operation, and Idempotency-Key.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BalanceMutationRequest'
            examples:
              debit:
                value:
                  account: '123456789012'
                  amount: 1.25
                  product_id: prepaid_uk
                  currency: GBP
      responses:
        '200':
          description: Balance debited.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
            X-Transaction-Id:
              $ref: '#/components/headers/TransactionId'
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/BalanceMutationResponse'
                - type: object
                  properties:
                    cgrid:
                      type: string
                      description: Billing event identifier for the debit.
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '500':
          $ref: '#/components/responses/ServerError'
  /create_account:
    post:
      tags:
      - Accounts
      operationId: createAccount
      summary: Create account
      description: 'Creates a prepaid account for the authenticated tenant. This operation
        is

        idempotent by tenant, operation, and Idempotency-Key.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAccountRequest'
            examples:
              with_cli:
                value:
                  product_id: prepaid_uk
                  initial_balance: 0
                  requires_pin: true
                  external_user_id: '+441234567890'
      responses:
        '201':
          description: Account created.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
            X-Transaction-Id:
              $ref: '#/components/headers/TransactionId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateAccountResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '502':
          $ref: '#/components/responses/UpstreamError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /delete_account:
    post:
      tags:
      - Accounts
      operationId: deleteAccount
      summary: Deactivate account
      description: 'Soft-deletes or deactivates an account. This does not hard-delete
        account

        rows. Idempotent by tenant, operation, and Idempotency-Key.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeleteAccountRequest'
            examples:
              deactivate:
                value:
                  account: '123456789012'
                  product_id: prepaid_uk
                  reason: customer requested closure
      responses:
        '200':
          description: Account deactivated, or already inactive.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
            X-Transaction-Id:
              $ref: '#/components/headers/TransactionId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteAccountResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '502':
          $ref: '#/components/responses/UpstreamError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /change_cli:
    post:
      tags:
      - Accounts
      operationId: changeCli
      summary: Change account caller ID
      description: 'Updates the account external_user_id, also referred to as CLI
        or caller

        id. Clients may send either external_user_id or cli. If both are sent,

        they must match.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChangeCliRequest'
            examples:
              change_cli:
                value:
                  account: '123456789012'
                  product_id: prepaid_uk
                  cli: '+441234567890'
      responses:
        '200':
          description: CLI updated.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
            X-Transaction-Id:
              $ref: '#/components/headers/TransactionId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChangeCliResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '502':
          $ref: '#/components/responses/UpstreamError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /setup_call:
    post:
      tags:
      - Calls
      operationId: setupCall
      summary: Store number translation for a call
      description: 'Stores a short-lived number translation for the calling path.
        The default

        partner contract is Cognito authorization plus Lambda partner RBAC. An

        API Gateway key gate is not part of the default partner contract.

        '
      security:
      - cognitoBearer: []
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetupCallRequest'
            examples:
              setup:
                value:
                  a_number: '+441234567890'
                  b_number: '+442071234567'
                  access_number: '+442038355820'
                  account_number: '123456789012'
                  ttl: 300
                  product_id: prepaid_uk
                  currency: GBP
      responses:
        '200':
          description: Call setup saved.
          headers:
            X-Correlation-Id:
              $ref: '#/components/headers/CorrelationId'
            X-Transaction-Id:
              $ref: '#/components/headers/TransactionId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SetupCallResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  securitySchemes:
    cognitoBearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'Cognito JWT accepted by API Gateway.


        Cognito user pool ID, issuer URL, and partner app client ID are provided during

        onboarding — not in this public OpenAPI file. Use an ID token unless Seshnova

        approves a different token type. Token claims derive tenant scope; the token
        must

        include custom:tenant_id for partner access.

        '
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[\x20-\x7E]+$
      description: Printable ASCII key used to replay the same response for the same
        request body.
    CorrelationId:
      name: X-Correlation-Id
      in: header
      required: false
      schema:
        type: string
        maxLength: 128
      description: Optional caller-provided correlation id for logs and responses.
  headers:
    CorrelationId:
      description: Correlation id used for request tracing.
      schema:
        type: string
    TransactionId:
      description: Transaction id, normally the Idempotency-Key for idempotent writes.
      schema:
        type: string
  schemas:
    AccountCode12:
      type: string
      description: Account code. Digits are normalized to a left-padded 12-digit value
        by the Lambda.
      example: '123456789012'
    MoneyAmount:
      type: number
      minimum: 0
      exclusiveMinimum: true
      example: 10.0
    Currency:
      type: string
      pattern: ^[A-Z]{3}$
      example: GBP
    ErrorResponse:
      type: object
      additionalProperties: true
      properties:
        error:
          oneOf:
          - type: string
          - type: object
            additionalProperties: true
        message:
          type: string
        detail:
          type: string
        correlation_id:
          type: string
        transaction_id:
          type: string
    GetBalanceRequest:
      type: object
      additionalProperties: false
      required:
      - account
      properties:
        account:
          $ref: '#/components/schemas/AccountCode12'
        product_id:
          type: string
          maxLength: 64
          description: Optional when product context is supplied by token claims.
          example: prepaid_uk
        currency:
          $ref: '#/components/schemas/Currency'
    CallHistoryRequest:
      description: Same shape as get_balance; identifies the account under tenant/product/currency
        scope.
      allOf:
      - $ref: '#/components/schemas/GetBalanceRequest'
    CallHistoryRow:
      type: object
      additionalProperties: false
      required:
      - called_number
      - duration_seconds
      - amount_charged
      properties:
        called_number:
          type: string
          description: Dialed destination for the billed call.
        duration_seconds:
          type: integer
          minimum: 0
          description: Billable duration in whole seconds (derived from `usage`).
        amount_charged:
          type: number
          description: Monetary cost from `cdrs.cost` for the rated session.
    CallHistoryResponse:
      type: object
      additionalProperties: false
      required:
      - tenant_id
      - product_id
      - currency
      - account_code_12
      - calls
      properties:
        tenant_id:
          type: string
        product_id:
          type: string
        currency:
          $ref: '#/components/schemas/Currency'
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        calls:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/CallHistoryRow'
    BalanceMutationRequest:
      type: object
      additionalProperties: false
      required:
      - account
      - amount
      properties:
        account:
          $ref: '#/components/schemas/AccountCode12'
        amount:
          $ref: '#/components/schemas/MoneyAmount'
        product_id:
          type: string
          maxLength: 64
          description: Optional when product context is supplied by token claims.
          example: prepaid_uk
        currency:
          $ref: '#/components/schemas/Currency'
    GetBalanceResponse:
      type: object
      additionalProperties: true
      required:
      - tenant_id
      - product_id
      - currency
      - account_code_12
      - total_balance
      properties:
        tenant_id:
          type: string
          example: seshnova
        product_id:
          type: string
          example: prepaid_uk
        currency:
          $ref: '#/components/schemas/Currency'
        account:
          type: string
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        tenant:
          type: string
        total_balance:
          type: number
          example: 12.5
        balances:
          type: array
          items:
            type: object
            additionalProperties: true
            properties:
              id:
                type: string
              value:
                type: number
              weight:
                type: number
        balance_map:
          type: object
          additionalProperties: true
    BalanceMutationResponse:
      type: object
      additionalProperties: true
      properties:
        id:
          type: integer
        result:
          nullable: true
        error:
          nullable: true
        tenant_id:
          type: string
        product_id:
          type: string
        currency:
          $ref: '#/components/schemas/Currency'
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        balance_before:
          type: number
        balance_after:
          type: number
        ledger_logged:
          type: boolean
        transaction_id:
          type: string
        correlation_id:
          type: string
    CreateAccountRequest:
      type: object
      additionalProperties: false
      required:
      - product_id
      properties:
        product_id:
          type: string
          maxLength: 64
          example: prepaid_uk
        initial_balance:
          type: number
          minimum: 0
          default: 0
        requires_pin:
          type: boolean
          default: true
        external_user_id:
          type: string
          nullable: true
          maxLength: 255
          description: Optional caller id or partner user identifier.
        cli:
          type: string
          nullable: true
          maxLength: 255
          deprecated: true
          description: Alias for external_user_id when supported by the Lambda.
    CreateAccountResponse:
      type: object
      additionalProperties: true
      properties:
        tenant_id:
          type: string
        product_id:
          type: string
        currency:
          $ref: '#/components/schemas/Currency'
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        initial_balance:
          type: number
        requires_pin:
          type: boolean
        external_user_id:
          type: string
          nullable: true
        pin_12:
          type: string
          nullable: true
        correlation_id:
          type: string
        transaction_id:
          type: string
    DeleteAccountRequest:
      type: object
      additionalProperties: false
      required:
      - account
      properties:
        account:
          $ref: '#/components/schemas/AccountCode12'
        product_id:
          type: string
          nullable: true
          maxLength: 64
        reason:
          type: string
          nullable: true
          maxLength: 500
    DeleteAccountResponse:
      type: object
      additionalProperties: true
      properties:
        tenant_id:
          type: string
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        product_id:
          type: string
          nullable: true
        status:
          type: string
          example: deactivated
        deactivated_at:
          type: string
          format: date-time
        reason:
          type: string
          nullable: true
        correlation_id:
          type: string
        transaction_id:
          type: string
    ChangeCliRequest:
      type: object
      additionalProperties: false
      required:
      - account
      properties:
        account:
          $ref: '#/components/schemas/AccountCode12'
        product_id:
          type: string
          nullable: true
          maxLength: 64
        external_user_id:
          type: string
          maxLength: 255
          description: New caller id or external user identifier.
        cli:
          type: string
          maxLength: 255
          description: Alias for external_user_id. If both are sent, they must match.
    ChangeCliResponse:
      type: object
      additionalProperties: true
      properties:
        tenant_id:
          type: string
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        product_id:
          type: string
          nullable: true
        external_user_id:
          type: string
        correlation_id:
          type: string
        transaction_id:
          type: string
    SetupCallRequest:
      type: object
      additionalProperties: false
      required:
      - a_number
      - b_number
      - access_number
      - account_number
      - ttl
      properties:
        a_number:
          type: string
          description: Caller number. Digits are required; a leading plus is preserved.
          example: '+441234567890'
        b_number:
          type: string
          description: Destination number.
          example: '+442071234567'
        access_number:
          type: string
          description: Access number used for the call.
          example: '+442038355820'
        account_number:
          $ref: '#/components/schemas/AccountCode12'
        ttl:
          type: integer
          minimum: 1
          maximum: 86400
          description: Translation lifetime in seconds.
          example: 300
        product_id:
          type: string
          maxLength: 64
          description: Optional when product context is supplied by token claims.
          example: prepaid_uk
        currency:
          $ref: '#/components/schemas/Currency'
    SetupCallResponse:
      type: object
      additionalProperties: true
      properties:
        message:
          type: string
          example: Call setup saved successfully
        insertId:
          type: integer
        tenant_id:
          type: string
        product_id:
          type: string
        currency:
          $ref: '#/components/schemas/Currency'
        account_code_12:
          $ref: '#/components/schemas/AccountCode12'
        correlation_id:
          type: string
        transaction_id:
          type: string
  responses:
    BadRequest:
      description: Invalid request body, invalid idempotency key, tenant_id in body,
        or validation error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Missing or invalid authorization, inactive membership, tenant inactive,
        or insufficient route permission.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Requested account or resource was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Conflict:
      description: Business conflict such as duplicate external_user_id, or idempotency
        conflict.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    IdempotencyConflict:
      description: Idempotency-Key has already been used with a different request
        body.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: Idempotency key already used with a different request
    UpstreamError:
      description: The API could not complete an upstream dependency request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServiceUnavailable:
      description: A required internal dependency or Lambda configuration is unavailable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServerError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
