> ## Documentation Index
> Fetch the complete documentation index at: https://flow9.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Create SLA policie

> Reminders and escalations are stored with the policy. Honours `Idempotency-Key`. **Required scope:** `settings:configure`.



## OpenAPI

````yaml /api-reference/openapi.yaml post /v1/settings/sla-policies
openapi: 3.1.0
info:
  title: Wander CRM Public API
  version: 1.0.0
  description: >
    The Wander CRM Public API enables third-party systems to integrate with

    Flow9 Engage CRM. Create leads, manage activities, retrieve customer

    profiles, and onboard new companies programmatically.


    ## Authentication

    Two credential types are accepted (see `securitySchemes`):

    - an **API key** in the `x-api-key` header — `f9_live_<32 hex>` for live
      traffic, `f9_test_<32 hex>` for the test sandbox (legacy `wcrm_live_` /
      `wcrm_test_` keys still authenticate but are no longer issued). Keys are
      created in **Settings > API Keys** or via `POST /v1/api-keys`.
    - an **OAuth 2.1 access token** in `Authorization: Bearer <jwt>` — what MCP
      clients and OAuth integrations send. Same scopes, same tenant isolation.

    If both are present, `x-api-key` wins. A request with neither is `401` with
    a

    `WWW-Authenticate: Bearer` header.


    These endpoints are **public** (no credential) — they are mounted before the

    auth middleware and apply their own per-IP rate limiting:

    - `GET /v1/health`

    - `POST /v1/onboarding` (tenant signup)

    - `GET /v1/availability` (module availability by country)


    `/v1/api-keys` is the one exception to the two credential types above: it is

    authenticated by a Company_Admin **session token** (Bearer), because a

    freshly-onboarded tenant has no API key yet.


    ## Rate limiting and quotas

    Three independent controls apply; a request must pass all of them.


    - **Per key** — sliding windows **per minute** (default 60, counted per
      endpoint) and **per hour** (default 1000, across the key). The
      `rate_limit_per_day` value stored on a key is **not enforced** in v1; it is
      kept for forward compatibility only.
    - **Per tenant** — one ceiling across every key the company holds (default
      600/minute, plus an optional daily cap derived from the plan). This is a
      protective backstop, not a meter.
    - **Monthly quota** — calls counted against the plan's `api_call_cap` for
    the
      billing period. Exhausted: `429 QUOTA_EXCEEDED`; no subscription: `402`;
      quota engine unreachable: `503 QUOTA_UNAVAILABLE` (fails closed).

    Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and

    `X-RateLimit-Reset`, describing whichever of the per-key / per-tenant
    windows

    is closest to rejecting. A `429` adds `Retry-After`. A monthly-quota refusal

    adds `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining`. Full detail:

    `docs/api/rate-limits.md`.


    ## Response Envelope

    All responses use a consistent JSON envelope:

    ```json

    {
      "success": true | false,
      "data": { ... },          // present on success
      "error": { "code": "...", "message": "...", "details": ... }, // present on error
      "meta": { "request_id": "uuid", "timestamp": "ISO 8601" }
    }

    ```
  contact:
    name: Flow9 API Support
    email: api-support@flow9.online
  license:
    name: Proprietary
    url: https://flow9.online
servers:
  - url: https://mwxpyoqrtdfxubdotbmj.supabase.co/functions/v1/public-api
    description: Production API
security:
  - ApiKeyAuth: []
  - BearerAuth: []
tags:
  - name: Health
    description: Service health check
  - name: Onboarding
    description: Company and admin user onboarding
  - name: Packages
    description: The sellable package catalogue (plans and add-on modules)
  - name: API Keys
    description: >-
      Headless API-key bootstrap — mint, list and revoke keys with a session
      token
  - name: Billing
    description: >-
      Read the tenant's plan, seats, trial, entitled modules, API quota and
      prepaid balance
  - name: Users
    description: >-
      The people in the workspace — list for assignment, create, deactivate,
      change role
  - name: Access control
    description: >-
      Read-only view of the authz engine: roles, permission profiles, the module
      × action matrix and role data-scope rules
  - name: Org structure
    description: >-
      The org tree (regions, countries, branches, teams), branches, and which
      users sit on which node
  - name: Settings
    description: >-
      Pipeline configuration: lead statuses and deal stages, tags, sources,
      duplicate-check fields, SLA policies
  - name: Scheduling
    description: >-
      When people are free and booking them: availability, slots, offer tokens,
      holds, assignments, bookings
  - name: Search & reports
    description: Cross-object search, aggregation, pipeline breakdown and saved reports
  - name: Attachments
    description: 'Files on records: upload, list, short-lived download links, delete'
  - name: Campaigns
    description: >-
      Read campaigns, their channels, runs and delivery stats; a record's
      message timeline
  - name: Messages
    description: 'Draft-and-approve messaging: prepare a message for a person to send'
  - name: Suppression
    description: Sending numbers, the do-not-call list and the email suppression list
  - name: Customers
    description: Customer profile retrieval
  - name: Leads
    description: Lead creation and management
  - name: Activities
    description: Deal and lead activity tracking
  - name: Objects
    description: >-
      Discover the objects and fields a key can see, and manage tenant-defined
      fields
  - name: Records
    description: >-
      Generic record CRUD, batch writes and per-record notes, tasks and
      activities
  - name: Webhooks
    description: Webhook subscriptions, deliveries, replays and the event catalogue
  - name: Test mode
    description: Sandbox controls available to `f9_test_` keys only
paths:
  /v1/settings/sla-policies:
    post:
      tags:
        - Settings
      summary: Create SLA policie
      description: >-
        Reminders and escalations are stored with the policy. Honours
        `Idempotency-Key`. **Required scope:** `settings:configure`.
      operationId: createSlaPolicy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SlaPolicyRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      sla_policy:
                        $ref: '#/components/schemas/SlaPolicy'
                  meta:
                    $ref: '#/components/schemas/Meta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: '`ALREADY_EXISTS` when the name is taken.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '429':
          $ref: '#/components/responses/TooManyRequests'
components:
  schemas:
    SlaPolicyRequest:
      type: object
      description: >-
        On create: name, priority, entity_type, the three times and
        operational_hours are required. `reminders` / `escalations`, when
        present, replace the existing set.
      properties:
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        priority:
          type: string
          enum:
            - Urgent
            - High
            - Medium
            - Low
        entity_type:
          type: string
          enum:
            - lead
            - customer
            - deal
        entity_filter:
          type: object
        first_response_time:
          type: integer
          minimum: 1
        every_response_time:
          type: integer
          minimum: 1
        resolution_time:
          type: integer
          minimum: 1
        operational_hours:
          type: string
          enum:
            - business_hours
            - 24/7
        business_hours_start:
          type:
            - string
            - 'null'
        business_hours_end:
          type:
            - string
            - 'null'
        business_days:
          type: array
          items:
            type: integer
        is_active:
          type: boolean
        use_user_schedule:
          type: boolean
        reminders:
          type: array
          items:
            $ref: '#/components/schemas/SlaReminder'
        escalations:
          type: array
          items:
            $ref: '#/components/schemas/SlaEscalation'
    SlaPolicy:
      type: object
      required:
        - id
        - name
        - priority
        - entity_type
        - first_response_time
        - every_response_time
        - resolution_time
        - operational_hours
        - business_days
        - is_active
        - use_user_schedule
        - reminders
        - escalations
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        priority:
          type: string
          enum:
            - Urgent
            - High
            - Medium
            - Low
        entity_type:
          type: string
          enum:
            - lead
            - customer
            - deal
        entity_filter:
          type: object
        first_response_time:
          type: integer
          description: Minutes.
        every_response_time:
          type: integer
          description: Minutes.
        resolution_time:
          type: integer
          description: Minutes.
        operational_hours:
          type: string
          enum:
            - business_hours
            - 24/7
        business_hours_start:
          type:
            - string
            - 'null'
          description: HH:MM, business_hours policies only.
        business_hours_end:
          type:
            - string
            - 'null'
        business_days:
          type: array
          items:
            type: integer
          description: Weekday numbers, 0 = Sunday.
        is_active:
          type: boolean
        use_user_schedule:
          type: boolean
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
        reminders:
          type: array
          items:
            $ref: '#/components/schemas/SlaReminder'
        escalations:
          type: array
          items:
            $ref: '#/components/schemas/SlaEscalation'
    Meta:
      type: object
      properties:
        request_id:
          type: string
          format: uuid
        timestamp:
          type: string
          format: date-time
        rate_limit:
          type: object
          properties:
            limit:
              type: integer
            remaining:
              type: integer
            reset:
              type: integer
    ErrorBody:
      type: object
      required:
        - success
        - error
        - meta
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
              description: >
                Human-readable message. Never contains internal detail, stack
                traces

                or upstream provider text.
            details:
              description: Additional context (field errors, available values, etc.)
        meta:
          $ref: '#/components/schemas/Meta'
    SlaReminder:
      type: object
      required:
        - reminder_type
        - trigger_time
        - recipient_user_ids
      properties:
        id:
          type: string
          format: uuid
        reminder_type:
          type: string
          enum:
            - first_response
            - every_response
            - resolution
        trigger_time:
          type: integer
          description: Minutes before the deadline.
        recipient_user_ids:
          type: array
          items:
            type: string
            format: uuid
    SlaEscalation:
      type: object
      required:
        - escalation_level
        - trigger_after_minutes
        - recipient_user_ids
        - message_template
      properties:
        id:
          type: string
          format: uuid
        escalation_level:
          type: integer
        trigger_after_minutes:
          type: integer
        recipient_user_ids:
          type: array
          items:
            type: string
            format: uuid
        message_template:
          type:
            - string
            - 'null'
    ErrorCode:
      type: string
      description: >
        Machine-readable error code. Branch on this, not on `message` — messages
        are

        human-readable and may change without notice.
      enum:
        - MISSING_API_KEY
        - AUTH_INVALID_KEY
        - API_KEY_EXPIRED
        - API_KEY_REVOKED
        - FORBIDDEN_SCOPE
        - IP_NOT_ALLOWED
        - MODULE_DISABLED
        - RATE_LIMIT_EXCEEDED
        - QUOTA_EXCEEDED
        - QUOTA_UNAVAILABLE
        - VALIDATION_ERROR
        - MISSING_REQUIRED_FIELD
        - INVALID_FIELD_FORMAT
        - NOT_FOUND
        - ALREADY_EXISTS
        - CONFLICT
        - SEAT_LIMIT_REACHED
        - NO_BILLING_CUSTOMER
        - AGREEMENT_ACTIVE
        - CARD_CHARGE_FAILED
        - NO_ACTIVE_SUBSCRIPTION
        - IDEMPOTENCY_KEY_REQUIRED
        - IDEMPOTENCY_CONFLICT
        - IDEMPOTENCY_IN_PROGRESS
        - INTERNAL_ERROR
        - SERVICE_UNAVAILABLE
        - METHOD_NOT_ALLOWED
  responses:
    BadRequest:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: Request validation failed
              details:
                fields:
                  first_name: First name is required
            meta:
              request_id: 550e8400-e29b-41d4-a716-446655440000
              timestamp: '2026-02-15T10:00:00.000Z'
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
          example:
            success: false
            error:
              code: AUTH_INVALID_KEY
              message: Invalid API key
            meta:
              request_id: 550e8400-e29b-41d4-a716-446655440000
              timestamp: '2026-02-15T10:00:00.000Z'
    Forbidden:
      description: Insufficient scope, expired/revoked key, or IP not allowed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
    TooManyRequests:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
          example:
            success: false
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Rate limit exceeded. Retry after 12 seconds.
              details:
                limit: 60
                remaining: 0
                reset: 1718400000000
                retry_after: 12
            meta:
              request_id: 550e8400-e29b-41d4-a716-446655440000
              timestamp: '2026-02-15T10:00:00.000Z'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        API key from Settings > API Keys or `POST /v1/api-keys`. Format

        `f9_live_<32 hex chars>` (live) or `f9_test_<32 hex chars>` (sandbox —
        no

        real sends, ever). Legacy `wcrm_live_` / `wcrm_test_` keys still

        authenticate but are no longer issued. Shown once at creation; only a
        hash

        is stored.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        OAuth 2.1 access token (`Authorization: Bearer <jwt>`), issued by the

        Flow9 authorization server to MCP clients and OAuth integrations.
        Carries

        the same `module:action` scopes as an API key and is subject to the same

        rate limits and tenant isolation. On `/v1/api-keys` ONLY, this header

        carries a Company_Admin **session token** instead.

````