openapi: 3.1.0
info:
  title: ZipQuantum API
  version: '2026-08-21'
  description: Machine-readable contract for the supported public and user API-key endpoints. Internal administration and
    edge-gateway routes are intentionally excluded.
  license:
    name: Proprietary
    url: https://zq.tn/terms/
servers:
- url: https://a.zq.tn/api
  description: Production
externalDocs:
  description: Official mobile integration examples
  url: https://github.com/ProxiwebLabs/zipquantum-mobile-examples
security: []
paths:
  /check-available-sub:
    post:
      operationId: checkManagedSubdomainAvailability
      summary: Check a managed subdomain
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubdomainAvailabilityRequest'
      responses:
        '200':
          description: Availability result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubdomainAvailabilityResponse'
        '409':
          description: Unavailable subdomain
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubdomainAvailabilityResponse'
        '422':
          $ref: '#/components/responses/ValidationError'
  /plans/catalog:
    get:
      operationId: listPublicPlans
      summary: List the public plan catalogue
      responses:
        '200':
          description: Active plans and prices
          content:
            application/json:
              schema:
                type: object
                required:
                - plans
                properties:
                  plans:
                    type: array
                    items:
                      $ref: '#/components/schemas/Plan'
        '429':
          $ref: '#/components/responses/Error'
  /createlink:
    post:
      operationId: createAnonymousLink
      summary: Create an anonymous Smart Link
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkCreateRequest'
      responses:
        '201':
          description: Link created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnonymousLinkCreateResponse'
        '403':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
  /anonymous-link-claims:
    post:
      operationId: createAnonymousLinkClaim
      summary: Start an anonymous-link claim
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnonymousLinkClaimRequest'
      responses:
        '201':
          description: Claim session created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnonymousLinkClaimResponse'
        '422':
          $ref: '#/components/responses/ValidationError'
  /v1/links:
    get:
      operationId: listLinks
      summary: List Smart Links owned by the API-key account
      security:
      - userApiKey: []
      parameters:
      - name: search
        in: query
        schema:
          type: string
          maxLength: 100
      - name: status
        in: query
        schema:
          type: string
          enum:
          - active
          - scheduled
          - expired
          - exhausted
      - name: created_from
        in: query
        schema:
          type: string
          format: date
      - name: created_to
        in: query
        schema:
          type: string
          format: date
      - name: sort
        in: query
        schema:
          type: string
          enum:
          - created_at
          - updated_at
          - clicks
          - code
      - name: direction
        in: query
        schema:
          type: string
          enum:
          - asc
          - desc
      - name: page
        in: query
        schema:
          type: integer
          minimum: 1
      - name: per_page
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 100
      responses:
        '200':
          description: Paginated links
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LinkListResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
    post:
      operationId: createLink
      summary: Create a Smart Link for the API-key account
      security:
      - userApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkCreateRequest'
      responses:
        '201':
          description: Link created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LinkCreateResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
  /v1/links/{link}:
    parameters:
    - name: link
      in: path
      required: true
      schema:
        type: integer
    get:
      operationId: getLink
      summary: Get an owned Smart Link
      security:
      - userApiKey: []
      responses:
        '200':
          description: Smart Link
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ManagedLink'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
    patch:
      operationId: updateLink
      summary: Update an owned Smart Link
      security:
      - userApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkUpdateRequest'
      responses:
        '200':
          description: Updated Smart Link
          content:
            application/json:
              schema:
                type: object
                properties:
                  link:
                    $ref: '#/components/schemas/ManagedLink'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteLink
      summary: Delete an owned Smart Link
      security:
      - userApiKey: []
      responses:
        '204':
          description: Smart Link deleted
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
  /v1/links/{link}/qr:
    post:
      operationId: generateLinkQrCode
      summary: Generate a branded QR code for an owned Smart Link
      security:
      - userApiKey: []
      parameters:
      - name: link
        in: path
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: Base64 data URI containing an SVG QR code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QrCodeResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
  /v1/links/{link}/analytics:
    get:
      operationId: getLinkAnalytics
      summary: Get analytics for an owned Smart Link
      description: Requires full_api_access and basic_analytics in the current plan.
      security:
      - userApiKey: []
      parameters:
      - name: link
        in: path
        required: true
        schema:
          type: integer
      - name: date_from
        in: query
        schema:
          type: string
          format: date
      - name: date_to
        in: query
        schema:
          type: string
          format: date
      responses:
        '200':
          description: Link analytics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnalyticsResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
  /v1/analytics:
    get:
      operationId: getAccountAnalytics
      summary: Get aggregate analytics for the API-key account
      description: Requires full_api_access and analytics_dashboard in the current plan.
      security:
      - userApiKey: []
      parameters:
      - name: reference
        in: query
        schema:
          type: string
      - name: date_from
        in: query
        schema:
          type: string
          format: date
      - name: date_to
        in: query
        schema:
          type: string
          format: date
      responses:
        '200':
          description: Account analytics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnalyticsResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
  /v1/account/usage:
    get:
      operationId: getAccountUsage
      summary: Get account usage and plan limits
      security:
      - userApiKey: []
      responses:
        '200':
          description: Usage meters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountUsageResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
  /v1/account/capabilities:
    get:
      operationId: getAccountCapabilities
      summary: Get the capabilities enabled by the current plan
      security:
      - userApiKey: []
      responses:
        '200':
          description: Plan capabilities
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountCapabilitiesResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
  /v1/mcp/context:
    get:
      operationId: getMcpContext
      summary: Validate MCP access and return the authenticated account context
      description: OAuth 2.1 access token restricted to the ZipQuantum MCP resource. Requires both full_api_access and mcp_access
        in the current plan.
      security:
      - mcpOAuth:
        - mcp
      responses:
        '200':
          description: MCP account context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpContextResponse'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
  /v1/custom-domains:
    get:
      operationId: listCustomDomains
      summary: List verified domains owned by the API-key account
      security:
      - userApiKey: []
      responses:
        '200':
          description: Verified domains
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomDomainListResponse'
        '401':
          $ref: '#/components/responses/Error'
  /deferred/recover:
    post:
      operationId: recoverDeferredLink
      summary: Recover a consented Android deferred deep link
      security:
      - userApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeferredRecoveryRequest'
      responses:
        '200':
          description: Recovery result
        '401':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
components:
  securitySchemes:
    userApiKey:
      type: apiKey
      in: header
      name: X-Api-Key
    mcpOAuth:
      type: oauth2
      description: OAuth 2.1 authorization code flow with PKCE S256 and the https://a.zq.tn/mcp resource audience.
      flows:
        authorizationCode:
          authorizationUrl: https://a.zq.tn/oauth/authorize
          tokenUrl: https://a.zq.tn/oauth/token
          scopes:
            mcp: Manage the consenting account through ZipQuantum MCP
  responses:
    Error:
      description: Operation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
  schemas:
    LinkParameter:
      type: object
      properties:
        name:
          type:
          - string
          - 'null'
          maxLength: 50
        value:
          type:
          - string
          - 'null'
          maxLength: 255
    DeviceTarget:
      type: object
      properties:
        device:
          type:
          - string
          - 'null'
          enum:
          - windows
          - macos
          - linux
          - ubuntu
          - ios
          - iphone
          - ipad
          - android
          - blackberry
          - other
          - null
        url:
          type:
          - string
          - 'null'
          format: uri
          maxLength: 2048
    LinkCreateRequest:
      type: object
      required:
      - link
      anyOf:
      - required:
        - subdomain
      - required:
        - managed_subdomain
      - required:
        - custom_domain
      - required:
        - custom_domain_id
      properties:
        subdomain:
          type: string
          minLength: 5
          maxLength: 63
          pattern: ^(?!-)[a-z0-9-]+(?<!-)$
        managed_subdomain:
          type: string
          minLength: 5
          maxLength: 63
        reference:
          type: string
          maxLength: 255
          pattern: ^[a-z0-9-]+$
        code:
          type: string
          maxLength: 255
          pattern: ^[a-z0-9-]+$
          deprecated: true
        custom_domain:
          type: string
          maxLength: 253
        custom_domain_id:
          type: integer
        link:
          type: string
          format: uri
          maxLength: 2048
        fallback_url:
          type:
          - string
          - 'null'
          format: uri
          maxLength: 2048
        apn:
          type:
          - string
          - 'null'
          maxLength: 255
        ios_store_url:
          type:
          - string
          - 'null'
          format: uri
          maxLength: 2048
        st:
          type:
          - string
          - 'null'
          maxLength: 255
        sd:
          type:
          - string
          - 'null'
          maxLength: 255
        si:
          type:
          - string
          - 'null'
          format: uri
          maxLength: 2048
        active_from:
          type:
          - string
          - 'null'
          format: date
        expires_at:
          type:
          - string
          - 'null'
          format: date
        click_limit:
          type:
          - integer
          - 'null'
          minimum: 1
        expiration_redirect_url:
          type:
          - string
          - 'null'
          format: uri
          maxLength: 2048
        parameters:
          type: array
          items:
            $ref: '#/components/schemas/LinkParameter'
        device_targets:
          type: array
          items:
            $ref: '#/components/schemas/DeviceTarget'
      example:
        subdomain: yourbrand
        reference: summer-campaign
        link: https://example.com/landing
        parameters:
        - name: utm_source
          value: newsletter
    LinkUpdateRequest:
      type: object
      minProperties: 1
      properties:
        reference:
          type: string
          maxLength: 255
          pattern: ^[a-z0-9-]+$
        link:
          type: string
          format: uri
          maxLength: 2048
        fallback_url:
          type:
          - string
          - 'null'
          format: uri
        subdomain:
          type:
          - string
          - 'null'
          minLength: 5
          maxLength: 63
        custom_domain:
          type:
          - string
          - 'null'
          maxLength: 253
        apn:
          type:
          - string
          - 'null'
          maxLength: 255
        ios_store_url:
          type:
          - string
          - 'null'
          format: uri
        st:
          type:
          - string
          - 'null'
          maxLength: 255
        sd:
          type:
          - string
          - 'null'
          maxLength: 255
        si:
          type:
          - string
          - 'null'
          format: uri
          maxLength: 2048
        active_from:
          type:
          - string
          - 'null'
          format: date-time
        expires_at:
          type:
          - string
          - 'null'
          format: date-time
        click_limit:
          type:
          - integer
          - 'null'
          minimum: 1
        expiration_redirect_url:
          type:
          - string
          - 'null'
          format: uri
        parameters:
          type: array
          items:
            $ref: '#/components/schemas/LinkParameter'
        device_targets:
          type: array
          items:
            $ref: '#/components/schemas/DeviceTarget'
    Link:
      type: object
      additionalProperties: true
      required:
      - id
      - code
      - destination_url
      properties:
        id:
          type: integer
        user_id:
          type:
          - integer
          - 'null'
        custom_domain_id:
          type:
          - integer
          - 'null'
        managed_subdomain:
          type:
          - string
          - 'null'
        code:
          type: string
        destination_url:
          type: string
          format: uri
        fallback_url:
          type:
          - string
          - 'null'
          format: uri
        android_package:
          type:
          - string
          - 'null'
        ios_store_url:
          type:
          - string
          - 'null'
          format: uri
        clicks:
          type: integer
        active_from:
          type:
          - string
          - 'null'
          format: date-time
        expires_at:
          type:
          - string
          - 'null'
          format: date-time
        click_limit:
          type:
          - integer
          - 'null'
        is_anonymous:
          type: boolean
    LinkCreateResponse:
      type: object
      required:
      - link
      - short_link
      properties:
        link:
          $ref: '#/components/schemas/Link'
        short_link:
          type: string
          format: uri
    ManagedLink:
      type: object
      required:
      - id
      - reference
      - short_link
      - destination_url
      - clicks
      - status
      properties:
        id:
          type: integer
        reference:
          type: string
        code:
          type: string
        short_link:
          type: string
          format: uri
        destination_url:
          type: string
          format: uri
        fallback_url:
          type:
          - string
          - 'null'
          format: uri
        custom_domain_id:
          type:
          - integer
          - 'null'
        custom_domain:
          type:
          - string
          - 'null'
        managed_subdomain:
          type:
          - string
          - 'null'
        clicks:
          type: integer
        click_limit:
          type:
          - integer
          - 'null'
        status:
          type: string
          enum:
          - active
          - scheduled
          - expired
          - exhausted
        parameters:
          type: array
          items:
            $ref: '#/components/schemas/LinkParameter'
        device_targets:
          type: array
          items:
            $ref: '#/components/schemas/DeviceTarget'
        created_at:
          type:
          - string
          - 'null'
          format: date-time
        updated_at:
          type:
          - string
          - 'null'
          format: date-time
      additionalProperties: true
    LinkListResponse:
      type: object
      required:
      - data
      - links
      - meta
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ManagedLink'
        links:
          type: object
        meta:
          type: object
    QrCodeResponse:
      type: object
      required:
      - link_id
      - short_link
      - qr
      - extension
      properties:
        link_id:
          type: integer
        short_link:
          type: string
          format: uri
        qr:
          type: string
          description: Base64 SVG data URI
        extension:
          type: string
          const: svg
    AnalyticsResponse:
      type: object
      required:
      - summary
      - trend
      - devices
      - platforms
      properties:
        link:
          type: object
        filters:
          type: object
        summary:
          type: object
          additionalProperties:
            type:
            - integer
            - number
        top_links:
          type: array
          items:
            type: object
        trend:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsRow'
        devices:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsRow'
        operating_systems:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsRow'
        browsers:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsRow'
        timezones:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsRow'
        platforms:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsRow'
    AnalyticsRow:
      type: object
      required:
      - label
      - total
      properties:
        label:
          type:
          - string
          - 'null'
        total:
          type: integer
    AccountUsageResponse:
      type: object
      required:
      - plan
      - usage
      - data_retention_days
      properties:
        plan:
          type: object
        usage:
          type: object
        data_retention_days:
          type: integer
    AccountCapabilitiesResponse:
      type: object
      required:
      - plan
      - capabilities
      properties:
        plan:
          type: object
        capabilities:
          type: object
          additionalProperties:
            type: boolean
    McpContextResponse:
      type: object
      required:
      - account
      - plan
      - capabilities
      properties:
        account:
          type: object
          properties:
            id:
              type: integer
            name:
              type: string
        plan:
          type: object
        capabilities:
          type: object
          additionalProperties:
            type: boolean
    AnonymousLinkCreateResponse:
      allOf:
      - $ref: '#/components/schemas/LinkCreateResponse'
      - type: object
        required:
        - claim_key
        - anonymous_claim
        properties:
          claim_key:
            type: string
            writeOnly: true
          anonymous_claim:
            type: object
            properties:
              claim_key:
                type: string
                writeOnly: true
              expires_at:
                type:
                - string
                - 'null'
                format: date-time
    CustomDomain:
      type: object
      required:
      - id
      - domain
      - status
      - links_count
      properties:
        id:
          type: integer
        domain:
          type: string
        status:
          type: string
          const: verified
        verified_at:
          type:
          - string
          - 'null'
          format: date-time
        links_count:
          type: integer
    CustomDomainListResponse:
      type: object
      required:
      - domains
      properties:
        domains:
          type: array
          items:
            $ref: '#/components/schemas/CustomDomain'
    SubdomainAvailabilityRequest:
      type: object
      properties:
        domain:
          type: string
          example: yourbrand.zq.tn
        code:
          type: string
          deprecated: true
      anyOf:
      - required:
        - domain
      - required:
        - code
    SubdomainAvailabilityResponse:
      type: object
      required:
      - available
      - message
      properties:
        available:
          type: boolean
        message:
          type: string
        name:
          type: string
        hostname:
          type: string
        normalized_code:
          type: string
        domain:
          type: string
    AnonymousLinkClaimRequest:
      type: object
      required:
      - links
      properties:
        source:
          type:
          - string
          - 'null'
          maxLength: 80
        links:
          type: array
          minItems: 1
          maxItems: 20
          items:
            type: object
            required:
            - id
            - claim_key
            properties:
              id:
                type: integer
              claim_key:
                type: string
                maxLength: 255
    AnonymousLinkClaimResponse:
      type: object
      required:
      - redirect_url
      - accepted_link_ids
      properties:
        redirect_url:
          type: string
          format: uri
        claim_expires_at:
          type:
          - string
          - 'null'
          format: date-time
        accepted_link_ids:
          type: array
          items:
            type: integer
    DeferredRecoveryRequest:
      type: object
      required:
      - tracking_consent
      properties:
        fingerprint_id:
          type:
          - string
          - 'null'
          maxLength: 255
        model:
          type:
          - string
          - 'null'
          maxLength: 255
        os_name:
          type:
          - string
          - 'null'
          maxLength: 255
        os_version:
          type:
          - string
          - 'null'
          maxLength: 255
        browser:
          type:
          - string
          - 'null'
          maxLength: 255
        screen_resolution:
          type:
          - string
          - 'null'
          maxLength: 255
        language:
          type:
          - string
          - 'null'
          maxLength: 255
        timezone:
          type:
          - string
          - 'null'
          maxLength: 255
        platform:
          type:
          - string
          - 'null'
          maxLength: 255
        tracking_consent:
          type: boolean
        consent_version:
          type:
          - string
          - 'null'
          maxLength: 32
    PlanPrice:
      type:
      - object
      - 'null'
      properties:
        amount:
          type: number
        currency:
          type: string
        active:
          type: boolean
        checkout_available:
          type: boolean
    Plan:
      type: object
      required:
      - id
      - name
      - prices
      - features
      - limits
      - capabilities
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type:
          - string
          - 'null'
        headline:
          type:
          - string
          - 'null'
        description:
          type:
          - string
          - 'null'
        featured:
          type: boolean
        display_order:
          type: integer
        prices:
          type: object
          properties:
            monthly:
              $ref: '#/components/schemas/PlanPrice'
            quarterly:
              $ref: '#/components/schemas/PlanPrice'
            annual:
              $ref: '#/components/schemas/PlanPrice'
        features:
          type: array
          items:
            type: string
        limits:
          type: object
          additionalProperties:
            type:
            - integer
            - 'null'
        capabilities:
          type: object
          additionalProperties:
            type: boolean
        support:
          type: string
    Error:
      type: object
      required:
      - message
      properties:
        message:
          type: string
      additionalProperties: true
    ValidationError:
      allOf:
      - $ref: '#/components/schemas/Error'
      - type: object
        required:
        - errors
        properties:
          errors:
            type: object
            additionalProperties:
              type: array
              items:
                type: string
