Crescendo Lab Broadcast API

Bulk (1-to-many) SMS. Small batches dispatch inline; >30 recipients queue asynchronously.

OpenAPI Specification

crescendo-lab-broadcast-api-openapi.yml Raw ↑
openapi: 3.0.3
info:
  title: MAAC Go Broadcast API
  version: 1.0.0
  description: "MAAC Go — Taiwan-first self-serve SMS platform. Send transactional (OTP /\norder / reminder) and broadcast SMS with NCC compliance, MAAC delivery\nbackend, and real-time DLR webhooks.\n\n- Base URL: `https://sms.cresclab.com/api`\n- Auth: `Authorization: Bearer <sk_live_... or sk_test_...>`\n- New accounts receive NT$50 trial credit on signup, enough to send real test SMS immediately.\n- API keys are scoped and wallet-backed; sending debits the account balance unless your deployment uses a mock SMS/payment adapter.\n- All phone numbers use E.164 (`+886912345678`). 09xxxxxxxx is also accepted.\n- Cost: NT$0.78 per SMS segment (Chinese: 70 chars/segment · English: 160/segment).\n- Rate-limit errors return `429 rate_limited` with limit/used details and retry guidance when available.\n- Delivery confirmations arrive asynchronously via webhook (`sms.delivered`\n  / `sms.failed`); poll `GET /sms/{id}` as a fallback.\n- MCP clients (Claude / Cursor / Windsurf) can use this spec directly, or\n  connect via `POST /api/mcp` (JSON-RPC 2.0) with the same bearer key.\n"
  contact:
    name: MAAC Go Support
    email: info@cresclab.com
    url: https://sms.cresclab.com
  license:
    name: Commercial
    url: https://sms.cresclab.com/terms.html
servers:
- url: https://sms.cresclab.com/api
  description: Production
security:
- bearerAuth: []
tags:
- name: Broadcast
  description: Bulk (1-to-many) SMS. Small batches dispatch inline; >30 recipients queue asynchronously.
paths:
  /broadcast:
    post:
      tags:
      - Broadcast
      summary: Create a broadcast (1-to-many)
      description: '≤ 30 recipients dispatch inline and return 200 with `delivered` / `failed` counts.

        \> 30 recipients queue async and return 202 with `status: "queued"`; cron dispatches in

        batches of 40 every 5 min; DLR webhooks flip individual messages.


        Schedule for later by passing `scheduled_at` (ISO 8601); funds reserve immediately,

        actual charge happens at send time.

        '
      operationId: createBroadcast
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BroadcastCreateRequest'
      responses:
        '200':
          description: Sent inline (small broadcast)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BroadcastSentResponse'
        '202':
          description: Queued for async dispatch (large broadcast)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BroadcastQueuedResponse'
        '400':
          description: recipients_required / invalid_phones / ncc_blocked / too_many_recipients
        '402':
          description: insufficient_balance
        '429':
          description: rate_limited
    get:
      tags:
      - Broadcast
      summary: List broadcasts with per-broadcast delivery stats
      operationId: listBroadcasts
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  broadcasts:
                    type: array
                    items:
                      $ref: '#/components/schemas/BroadcastSummary'
components:
  schemas:
    BroadcastQueuedResponse:
      type: object
      properties:
        ok:
          type: boolean
        broadcast_id:
          type: string
        status:
          type: string
          enum:
          - queued
        recipient_count:
          type: integer
        cost_cents:
          type: integer
        dispatch:
          type: string
          enum:
          - async
    BroadcastCreateRequest:
      type: object
      required:
      - body
      - recipients
      properties:
        name:
          type: string
          description: Internal label for the campaign
        body:
          type: string
          description: Same NCC rules as /sms/send
        recipients:
          type: array
          items:
            type: string
            description: E.164 or 09xxxxxxxx
          minItems: 1
          maxItems: 10000
        scheduled_at:
          type: string
          format: date-time
          description: ISO 8601. Omit to send immediately.
        from:
          type: string
        team:
          type: string
          description: Cost-attribution team for the whole broadcast (e.g. `電商`). Overrides the API key's bound team; an unknown name is auto-created.
          example: 電商
    BroadcastSentResponse:
      type: object
      properties:
        ok:
          type: boolean
        broadcast_id:
          type: string
        status:
          type: string
          enum:
          - sent
        recipient_count:
          type: integer
        delivered:
          type: integer
          description: Accepted by carrier — final status via webhook
        failed:
          type: integer
        cost_cents:
          type: integer
        refund_cents:
          type: integer
        balance_cents:
          type: integer
        team:
          type: string
          nullable: true
          description: Resolved cost-attribution team (null = 未分類).
        team_created:
          type: boolean
          description: True if this broadcast auto-created the team.
    BroadcastSummary:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        recipient_count:
          type: integer
        cost_cents:
          type: integer
        status:
          type: string
          enum:
          - scheduled
          - sending
          - sent
          - failed
        delivered_count:
          type: integer
        failed_count:
          type: integer
        sent_count:
          type: integer
        created_at:
          type: string
          format: date-time
        scheduled_at:
          type: string
          format: date-time
          nullable: true
        finished_at:
          type: string
          format: date-time
          nullable: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: '`Authorization: Bearer <api_key>`. Production keys start with `sk_live_`,

        test keys with `sk_test_` for environment separation. Sending still follows

        the account wallet and deployment adapter settings.

        '