Skip to main content

Import data into Sybill

Create sources and object types, import conversations, messages, documents, and custom rows, and manage imported records with the Sybill API's update and delete semantics.

Written by Sybill Inc


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:

  1. Create a source - a logical channel for your records (your support system, wiki, notes database). You do this once per system.

  2. Define an object type - only needed for rows, which are typed records.

  3. Import records - POST the record with your source's UUID and your own stable ID.

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

  • displayName is what users see in Sybill; you can update it any time with PATCH /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

id

yes

Your stable external identifier. Sybill identifies the record by the pair (sourceId, id) - reuse it to update or delete

sourceId

yes

UUID of a source you created

createdAt

conversations, messages

Source-system creation time. Optional for rows and documents

updatedAt

no

Source-system last-modified time

public

no

true makes the record readable by everyone in your organization. Defaults to false

ownerEmails

conditional

Emails of the record's owners. Private records (public: false) require at least one valid entry

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 needs text and an absolute timestamp (relative offsets are rejected), plus speakerId or speakerName. Every timestamp must fall between startedAt and endedAt.

  • recordingUrl - a URL Sybill can fetch to transcribe and enrich the audio or video. recordingMimeType is 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, or

  • content - 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. The fields map follows JSON merge-patch (RFC 7396): keys you send overwrite, keys you omit are preserved, and an explicit null deletes the field. Top-level fields (name, public, ownerEmails, updatedAt) are different: sending null for 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 new url or content creates a new version and reprocesses the document; supplying neither performs a metadata-only update.

  • Sources - PATCH /v1/sources/{sourceId} updates displayName only; name is 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.

Did this answer your question?