# Ingest API | Rippit Integration Docs

> 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.

Canonical: https://www.rippit.com/integrations/docs/ingest-api

Contents

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

| Type | Format | One row is | Matched on re-send by |
| --- | --- | --- | --- |
| Conversations | JSON Lines | One conversation with all its messages | Your conversation ID |
| Tables | Parquet (recommended) or JSON Lines | One record | The primary key you choose |

### Limits on the free tier

| Limit | Value |
| --- | --- |
| File size | 128 MB per upload |
| Uploads | 10 a minute, 100 an hour, 100 a day. Only completed uploads count |
| Messages per conversation | 10,000 |
| Columns per table | 500 |
| Higher limits | Ask 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

| Method | Path | What it does |
| --- | --- | --- |
| POST | /conversation\_sources | Create a source. Body: name, description. Returns created or exists, so it’s safe on every run |
| GET | /conversation\_sources | List 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}/start | Mark 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

| Field | Required | Type | What it’s for |
| --- | --- | --- | --- |
| conversation\_id | Yes | string, up to 96 characters | Your ID. Sending it again updates the conversation instead of duplicating it |
| started\_at | Yes | ISO 8601 with time zone | When the conversation started |
| ended\_at | No  | ISO 8601 | When it ended. Defaults to the last message’s time, and can’t be earlier than started\_at |
| searchable | No  | object, up to 50 keys | Your own fields for filtering and reports. Values are text, numbers, true or false, or empty. No lists |

### Message fields (events)

| Field | Required | Type | What it’s for |
| --- | --- | --- | --- |
| event\_id | Yes | string, up to 96 characters | Unique within the conversation. “1”, “2” and so on works |
| time | Yes | ISO 8601 | When the message was sent |
| author\_role | Yes | agent or customer | Who wrote it |
| body | Yes | string, up to 100,000 characters | The message text |
| author\_id | No  | string | Your ID for the author |
| author\_email | No  | string | The author’s email |
| author\_name | No  | string | Display name. Shows as Anonymous if missing |
| public | No  | true or false (default true) | false marks an internal note |
| attributes | No  | object, up to 20 keys | Your 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

| Method | Path | What it does |
| --- | --- | --- |
| POST | /datasets | Create a table. Safe to repeat with the same name |
| GET | /datasets | List 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\|jsonl | Request an upload slot |
| POST | /jobs/{job\_id}/start | Mark 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

| Code | Meaning | Fix |
| --- | --- | --- |
| 401 | Token missing, wrong or expired | Check the apitoken header, or create a new token |
| 403 | The API isn’t turned on for your workspace | Ask your Rippit contact |
| 404 | Source, table or job not found, or not in your account | Create it first, or check the job ID |
| 409 | Job started before the file finished uploading, or started twice | Upload first, then start once |
| 413 or 400 | File over 128 MB | Split the file |
| 422 | Bad table definition, like an invalid column name or type | Check detail in the response |
| 429 | Rate limit | Wait 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.

[Start free →](https://app.rippit.com/signup)[Book a demo](https://www.rippit.com/request-demo)

## Getting started from an AI agent

There are two ways to connect. Either works.

- MCP server: `https://mcp.rippit.com/mcp` (Streamable HTTP, OAuth). The person signs in or signs up in the browser that opens. No client ID, secret or API key is needed.
- CLI: install with `curl -fsSL https://rippit.sh | sh`, then get an account one of two ways:
  - `rippit login`: the person signs in or signs up in a browser.
  - `rippit start`: creates a temporary account with no email or browser, for a person with no Rippit account. `rippit claim --wait` lets them keep it; an unclaimed account is deleted after 14 days.

Then bring in data: connect a provider such as Zendesk, Intercom, Gong or Salesforce, or upload a CSV.

Full steps: https://www.rippit.com/setup.md
