Bulk ingest

Ingest API

Send conversations and tables from any system without a built-in integration. Bulk uploads on a schedule, hourly or daily. A real-time streaming API is planned.

Getting started

  1. 1

    Ask us to turn it on

    The API is enabled per workspace by your Rippit contact. Until then, every request returns 403.

  2. 2

    Create a token

    A Rippit admin goes to Tables > Add table > Add API Table > Create API token. You can also ask Rippit in chat to connect via API.

  3. 3

    Send it with every request

    Put the token in an apitoken header. Keep it out of scripts you share and out of AI chats.

Base URL
https://integrations.maestroqa.com/api/v2/bulk_ingest
OpenAPI schema
https://integrations.maestroqa.com/api/v2/openapi.json

How an upload works

  1. 1

    Create the destination

    A conversation source or a dataset. Calling it again with the same name is safe.

  2. 2

    Request an upload slot

    You get a one-time upload link that’s valid for 1 hour.

  3. 3

    Upload one file

    Up to 128 MB, sent straight to the upload link without your Rippit token.

  4. 4

    Start the job

    Mark the upload complete so processing starts.

  5. 5

    Check until it’s done

    Poll every 10 to 30 seconds until the job reads done or failed.

Test with about 10 conversations or 1,000 rows before sending everything.

What you can send

TypeFormatOne row isMatched on re-send by
ConversationsJSON LinesOne conversation with all its messagesYour conversation ID
TablesParquet (recommended) or JSON LinesOne recordThe primary key you choose

Limits on the free tier

LimitValue
File size128 MB per upload
Uploads10 a minute, 100 an hour, 100 a day. Only completed uploads count
Messages per conversation10,000
Columns per table500
Higher limitsAsk your Rippit contact or the Rippit Community

Conversations

One conversation per line in a JSON Lines file, with a conversation object and its events.

Endpoints

MethodPathWhat it does
POST/conversation_sourcesCreate a source. Body: name, description. Returns created or exists, so it’s safe on every run
GET/conversation_sourcesList sources with their columns and table link (empty until the first upload)
POST/conversation_blocks/bulk?source=<name>Request an upload slot. Returns job_id, upload_url, upload_fields, expires_at, max_bytes
POST/jobs/{job_id}/startMark the upload complete. Returns queued
GET/jobs/{job_id}Check the job until it’s done or failed

To upload, send a multipart POST to upload_url with every upload_fields entry unchanged and the file under the field name file. Don’t send your token. Success returns 204.

Conversation fields

FieldRequiredTypeWhat it’s for
conversation_idYesstring, up to 96 charactersYour ID. Sending it again updates the conversation instead of duplicating it
started_atYesISO 8601 with time zoneWhen the conversation started
ended_atNoISO 8601When it ended. Defaults to the last message’s time, and can’t be earlier than started_at
searchableNoobject, up to 50 keysYour own fields for filtering and reports. Values are text, numbers, true or false, or empty. No lists

Message fields (events)

FieldRequiredTypeWhat it’s for
event_idYesstring, up to 96 charactersUnique within the conversation. “1”, “2” and so on works
timeYesISO 8601When the message was sent
author_roleYesagent or customerWho wrote it
bodyYesstring, up to 100,000 charactersThe message text
author_idNostringYour ID for the author
author_emailNostringThe author’s email
author_nameNostringDisplay name. Shows as Anonymous if missing
publicNotrue or false (default true)false marks an internal note
attributesNoobject, up to 20 keysYour own fields on the message
  • Field names in searchable and attributes use letters, numbers and underscores, start with a letter or underscore, and are up to 64 characters.
  • A source’s columns are set by the searchable fields in its first upload. Use the same fields after that. If a file matches a different source better, the upload fails and the error names that source.
  • Each source becomes a table with one row per conversation.
Example line
{"conversation":{"conversation_id":"chat_8f2a","started_at":"2026-09-27T14:02:11Z","ended_at":"2026-09-27T14:09:40Z","searchable":{"channel":"web_chat","plan":"pro","csat":5}},"events":[{"event_id":"1","time":"2026-09-27T14:02:11Z","author_role":"customer","author_email":"sam@acme.com","author_name":"Sam Lee","body":"My invoice is wrong"},{"event_id":"2","time":"2026-09-27T14:03:05Z","author_role":"agent","author_id":"agent_42","author_name":"Alex Kim","body":"Sorry about that, let me check."}]}

Tables

Mirror flat data like accounts or orders, and join it to conversations.

Endpoints

MethodPathWhat it does
POST/datasetsCreate a table. Safe to repeat with the same name
GET/datasetsList tables with their keys, settings and table link
PATCH/datasets/{dataset_id}Rename it, label or hide columns, and link it to conversations
POST/datasets/{dataset_id}/bulk?format=parquet|jsonlRequest an upload slot
POST/jobs/{job_id}/startMark the upload complete
GET/jobs/{job_id}Check the job

Rules

  • Column types: int, float, string (store IDs as text), bool, datetime (converted to UTC) and list of any of those.
  • Column names use letters, numbers and underscores, start with a letter or underscore, and are up to 128 characters. Names starting with two underscores are reserved.
  • Update in place: set primary key columns. A row with an existing key replaces the stored one.
  • Keep every row: leave the key empty and turn on append-only. Uploading the same file twice creates duplicates.
  • You must pick one of the two, and the primary key can’t be changed later.
  • If you set an updated-at column, the later value wins a conflict. Otherwise the latest upload wins.
  • New columns in a later file are added automatically.
Create a table
{"name":"accounts","description":"One row per customer account, synced nightly from the billing database.","columns":[{"column":"account_id","type":"string"},{"column":"plan","type":"string"},{"column":"seats","type":"int"},{"column":"mrr","type":"float"},{"column":"is_active","type":"bool"},{"column":"updated_at","type":"datetime"}],"pkey_cols":["account_id"],"updated_at_col":"updated_at"}

Linking to conversations

  • To join a table to conversations, add a text column called conversation_id with IDs that match your conversation uploads, then set it as the conversation link. One or many rows per conversation both work.
  • Renaming changes the label only. The table’s ID and name stay the same.
PATCH /datasets/{dataset_id}
{"label":"Accounts (billing)","columns":{"mrr":{"label":"MRR (USD)"},"internal_notes":{"hidden":true}},"conversation_link_column":"conversation_id"}

When something goes wrong

Partial failures

  • Conversations and JSON Lines tables: each line is checked on its own. Bad lines come back with their line number (starting at 0) and the reason, and every other line is written. Fix and re-send just those lines.
  • Parquet tables: types must match exactly. A mismatch fails the job and names the column. Files that finished before it stay written.
  • Text that fits the type is accepted in JSON Lines, like “12” for a number.

Error codes

CodeMeaningFix
401Token missing, wrong or expiredCheck the apitoken header, or create a new token
403The API isn’t turned on for your workspaceAsk your Rippit contact
404Source, table or job not found, or not in your accountCreate it first, or check the job ID
409Job started before the file finished uploading, or started twiceUpload first, then start once
413 or 400File over 128 MBSplit the file
422Bad table definition, like an invalid column name or typeCheck detail in the response
429Rate limitWait the number of seconds in Retry-After, then retry

Let your AI assistant do it

The rippit-bulk-ingest-api skill walks an AI assistant through the whole thing: saving your token privately, checking access, a test upload, the full upload and a schedule. It works best in assistants that can run code, like Claude Code, Codex or Cursor. In a chat-only assistant, it writes the script for you to run.

Install in Claude Code
mkdir -p ~/.claude/skills/rippit-bulk-ingest-api
curl -sL https://integrations.maestroqa.com/api/v2/guides/rippit-bulk-ingest-api.md \
  -o ~/.claude/skills/rippit-bulk-ingest-api/SKILL.md
  • Claude web or desktop: save the file as SKILL.md in a folder named rippit-bulk-ingest-api, zip the folder, and upload it under Settings > Capabilities > Skills.
  • Other assistants: put rippit-bulk-ingest-api/SKILL.md in the assistant’s skills folder.
  • Start a new session after installing. Run the same command again to update.
No skills support? Paste this
Read https://integrations.maestroqa.com/api/v2/guides/rippit-bulk-ingest-api.md and follow it to help me send my data into Rippit.

Your data and creativity is your edge. Put agents on it to maximize it.

Connect your data. Launch agents.