Splashify RCS

Docs

Open panel
Panel

Reports

Two flavours:

  • Summary, fast synchronous read. KPI roll-up + daily series + in-range broadcast table. Use it for dashboards.
  • Export, asynchronous. Queues a CSV or XLSX file that the export worker uploads to storage and emails you a signed download link. Files retain for 7 days.

Every endpoint is scoped by the caller's bots JWT claim. A viewer or operator sees only their assigned bots. Passing bot_id="*" (or omitting it) means "all bots the caller can act on".

http
GET  /api/v1/rcs/reports/summary
POST /api/v1/rcs/reports/export
GET  /api/v1/rcs/reports/exports
GET  /api/v1/rcs/reports/exports/{export_id}

GET /rcs/reports/summary

Auth: Authorization: Bearer <jwt> (any role).

Query params:

Maximum window: 90 days (400 invalid_date_range beyond).

Success:

json
{
  "success": true,
  "from":    "2026-06-15",
  "to":      "2026-07-14",
  "scope":   ["*"],
  "kpis": {
    "total_broadcasts": 12,
    "total_recipients": 184320,
    "sent":             175430,
    "delivered":        168112,
    "read":             94207,
    "clicked":          14208,
    "failed":           8890,
    "ctr":              8.45,
    "delivery_rate":    95.83,
    "cost":             1247.85
  },
  "series": [
    { "date": "2026-06-15", "sent": 0,     "delivered": 0,     "failed": 0   },
    { "date": "2026-06-16", "sent": 2105,  "delivered": 2001,  "failed": 12  },
    { "date": "2026-07-14", "sent": 18324, "delivered": 17501, "failed": 92  }
  ],
  "broadcasts": [
    {
      "broadcast_id":    "b1b2…",
      "name":            "May launch",
      "bot_id":          "e8bb61e0-…",
      "bot_name":        "acme_promotions",
      "status":          "completed",
      "total_count":     18324,
      "sent_count":      18200,
      "delivered_count": 17501,
      "read_count":      9820,
      "clicked_count":   1247,
      "failed_count":    124,
      "started_at":      "2026-07-14T08:00:01Z",
      "completed_at":    "2026-07-14T08:22:44Z"
    }
  ]
}

Errors:

  • 400 {"success": false, "code": "invalid_date_range", "message": "to must be on or after from"}
  • 400 {"success": false, "code": "invalid_date_range", "message": "Range too wide. The maximum is 90 days."}
  • 403 {"success": false, "code": "bot_not_assigned", "message": "You don't have access to this bot."}

POST /rcs/reports/export

Queue an async export. The worker picks it up within 15 s (average), uploads the file to storage, and emails a signed download link.

Auth: Authorization: Bearer <jwt> (any role, data is scoped).

Request body:

Example:

json
{
  "bot_id": "e8bb61e0-8500-11ef-9c9a-4bc60d7a1e2f",
  "from":   "2026-06-15",
  "to":     "2026-07-14",
  "format": "xlsx",
  "scope":  "targets"
}

Success:

json
{
  "success":    true,
  "export_id":  "c1c2c3…",
  "status":     "queued",
  "expires_at": "2026-07-21T12:03:44Z"
}

Errors:

  • 400 {"success": false, "message": "format must be csv or xlsx"}
  • 400 {"success": false, "message": "scope must be 'broadcasts' or 'targets'"}
  • 400 {"success": false, "code": "invalid_date_range", "message": "…"}
  • 403 {"success": false, "code": "bot_not_assigned", …}
  • 429 {"success": false, "code": "too_many_exports_queued", "message": "You already have too many exports queued. Wait for one to finish then try again."}

GET /rcs/reports/exports

List recent exports (last 30 days), newest first.

Auth: Authorization: Bearer <jwt>.

Query params:

Success:

json
{
  "success": true,
  "exports": [
    {
      "export_id":         "c1c2c3…",
      "requested_by":      "[email protected]",
      "requested_by_role": "owner",
      "bot_id_scope":      "e8bb61e0-…",
      "from_ts":           "2026-06-15T00:00:00Z",
      "to_ts":             "2026-07-14T00:00:00Z",
      "format":            "xlsx",
      "scope":             "targets",
      "status":            "ready",
      "download_url":      "https://folder.splashifypro.com/rcs-exports/aaaa…/c1c2c3….xlsx",
      "file_size_bytes":   482114,
      "error_message":     "",
      "requested_at":      "2026-07-14T12:03:44Z",
      "completed_at":      "2026-07-14T12:04:07Z",
      "expires_at":        "2026-07-21T12:03:44Z"
    }
  ]
}

GET /rcs/reports/exports/{export_id}

Fetch one export's current state (poll for queued → running → ready / failed).

Success: same shape as one row of GET /reports/exports.

Errors:

  • 400 {"success": false, "message": "invalid export_id"}
  • 404 {"success": false, "code": "export_not_found", "message": "Export not found. It may have expired."}

Export lifecycle

text
queued  →  running  →  ready   (download_url populated, email sent)
                    ↘
                     failed    (error_message populated)
  • queued, created; worker hasn't started yet.
  • running, worker is streaming data + uploading.
  • ready, file uploaded. download_url is a public link, valid until expires_at (7 days after requested_at).
  • failed, see error_message.

The "Your RCS report is ready" email fires once when status transitions to ready. No email fires for failed, the panel surfaces the error inline.

Retention: after expires_at both the downloadable file and its record are deleted. Polling that export_id afterwards returns 404 export_not_found.

CSV column reference

scope=broadcasts

One row per broadcast in the window.

Sample row:

text
b1b2c3d4-…, "May launch", acme_promotions, completed, 18324, 18200, 17501, 9820, 1247, 124, 17290.00, 2026-07-14T08:00:01Z, 2026-07-14T08:22:44Z

scope=targets

One row per recipient across every broadcast in the window.

Sample row:

text
b1b2c3d4-…, "May launch", acme_promotions, +919876543210, delivered, 2026-07-14T08:00:02Z, 2026-07-14T08:00:03Z, 2026-07-14T08:07:41Z, ,

XLSX

Same columns as CSV, one sheet (Broadcasts or Targets), header row frozen, columns auto-sized best-effort. Content type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

Errors summary