With the Import permission (ingest scope), you can bring your own data into Sybill: call recordings and transcripts, messages, documents, and custom typed records. Sybill processes what you import - transcription, summaries, search indexing - and the records become part of your workspace.
How importing works
Every import follows the same pattern:
Create a source - a logical channel for your records (your support system, wiki, notes database). You do this once per system.
Define an object type - only needed for rows, which are typed records.
Import records -
POSTthe record with your source's UUID and your own stable ID.Update or delete later - identify the record by the same source UUID and ID.
A 201 Created response means Sybill stored the record and accepted it for asynchronous processing. Transcription, document conversion, summaries, and search availability may lag the import - don't expect derived fields immediately.
Step 1: Create a source
curl -X POST https://api.sybill.ai/v1/sources \ -H "Authorization: Bearer sk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "support_tickets", "displayName": "Support Tickets"}'name(1–255 chars) is a stable machine identifier, unique within your organization, and cannot be changed later.displayNameis what users see in Sybill; you can update it any time withPATCH /v1/sources/{sourceId}.
The response includes the source's UUID id - keep it. Every import request references it as sourceId.
Step 2 (rows only): Create an object type
Rows are custom typed records, so they need a schema first:
curl -X POST https://api.sybill.ai/v1/object-types \ -H "Authorization: Bearer sk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "sourceId": "YOUR_SOURCE_UUID", "name": "ticket", "displayName": "Ticket", "fieldDefinitions": [ {"name": "status", "fieldType": "string", "required": true}, {"name": "opened_at", "fieldType": "datetime"} ] }'An object type takes 1–50 field definitions with unique names. Field types: textarea, string, boolean, int, double, datetime, date, list. Updating an object type with new fieldDefinitions replaces the entire schema.
Fields shared by every import
All import bodies (POST /v1/conversations, /v1/messages, /v1/rows, /v1/documents) share these fields:
Field | Required | Meaning |
| yes | Your stable external identifier. Sybill identifies the record by the pair |
| yes | UUID of a source you created |
| conversations, messages | Source-system creation time. Optional for rows and documents |
| no | Source-system last-modified time |
| no |
|
| conditional | Emails of the record's owners. Private records ( |
Timestamps in import bodies accept ISO 8601 strings, Unix seconds, or Unix milliseconds — the format is auto-detected.
⚠️ API keys have no user context, so they cannot read private records back. If your integration needs to re-read what it imports, set public: true.
Import a conversation
POST /v1/conversations accepts either a pre-transcribed conversation, a recording for Sybill to transcribe, or both:
transcript- up to 10,000 sentences. Each needstextand an absolutetimestamp(relative offsets are rejected), plusspeakerIdorspeakerName. Every timestamp must fall betweenstartedAtandendedAt.recordingUrl- a URL Sybill can fetch to transcribe and enrich the audio or video.recordingMimeTypeis an optional hint.
At least one of the two must be provided. Other fields: startedAt (required), endedAt (optional, must be after startedAt and within 10 hours of it), participants (required, 1–1,000 people - each needs an id or email), and displayName for the title shown in Sybill.
curl -X POST https://api.sybill.ai/v1/conversations \ -H "Authorization: Bearer sk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "call-1042", "sourceId": "YOUR_SOURCE_UUID", "createdAt": "2026-08-01T14:00:00Z", "startedAt": "2026-08-01T14:00:00Z", "endedAt": "2026-08-01T14:45:00Z", "displayName": "Renewal call with Acme", "public": true, "participants": [{"name": "Jane Smith", "email": "[email protected]"}], "recordingUrl": "https://example.com/recordings/call-1042.mp4" }'
Import a message
POST /v1/messages requires body (plain text, up to 100,000 characters), sender, recipients (1–1,000), and createdAt. Optional: attachments (each with a name and a url Sybill can fetch), threadId to group messages into a thread, and displayName for the subject.
Import a document
POST /v1/documents requires exactly one of:
url- a URL Sybill can fetch, orcontent- the document bytes, base64-encoded.
contentType defaults to text/plain. For naming: displayName falls back to filename, then to the URL's filename for URL documents - content uploads must include displayName or filename. The optional author is set at import time and cannot be changed later; to change it, delete and re-import the document.
Import a row
POST /v1/rows requires objectTypeId, name (1–1,000 chars), and fields - 1–50 values keyed by field name. Each value carries the field's value and its fieldType, which must match the object type's declared type for that field.
Updating records
Rows -
PATCH /v1/rows?remoteId=YOUR_ID&sourceId=SOURCE_UUID. Thefieldsmap follows JSON merge-patch (RFC 7396): keys you send overwrite, keys you omit are preserved, and an explicitnulldeletes the field. Top-level fields (name,public,ownerEmails,updatedAt) are different: sendingnullfor them is ignored rather than clearing the value. Unknown keys, type mismatches, and deleting a required field are rejected.Documents -
PATCH /v1/documents?remoteId=YOUR_ID&sourceId=SOURCE_UUID. Supplying a newurlorcontentcreates a new version and reprocesses the document; supplying neither performs a metadata-only update.Sources -
PATCH /v1/sources/{sourceId}updatesdisplayNameonly;nameis immutable.Object types -
PATCH /v1/object-types/{objectTypeId}can update the display name or replace the full field schema.
Conversations and messages have no PATCH - delete and re-import to change them.
Deleting records
DELETE on /v1/conversations, /v1/messages, /v1/rows, and /v1/documents takes the same identity pair as query parameters:
curl -X DELETE \ "https://api.sybill.ai/v1/rows?remoteId=ticket-501&sourceId=YOUR_SOURCE_UUID" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
Deletion is a soft delete - the response confirms "status": "deleted" with a deletedAt timestamp.
Two structural deletes behave differently:
Deleting a source blocks future imports through it, but records and object types already created under it remain.
Deleting an object type keeps existing rows readable, but new rows and updates can no longer use it.
Request limits worth knowing
POST and PATCH requests require a Content-Length header and are capped at 10 MB. For large media, prefer recordingUrl/url over inline base64 content. See API pagination, rate limits, errors, and conventions for error formats and rate limits.
