Skip to documentation

Meet Zentrik Loops on Product Hunt.

See the launch

Signals API quickstart

Signals API quickstart

Send one transcript, ticket, review, or report to Zentrik, wait for processing, and confirm the resulting Signal in Discovery.

If you are deciding between manual import, a connector, and the API, start with Import evidence into Discovery. If you already know you want the API, this page shows the smallest working path:

  • create one signal
  • let Zentrik queue processing automatically
  • confirm the signal appears in Discovery
  • move to the production template when you are ready for batching or pagination

Before you start

You need three things:

  • a workspace API key from Settings → API Keys
  • your workspace base URL including /api
  • one real record to send, usually as a transcript, support ticket, review, or discovery report

For a product-level first test, use manual import in Discovery before automating anything.

Hello World

Start with one transcript. This example creates a signal and waits for processing to finish.

python47 lines
#!/usr/bin/env python3
import json
import os
import time
import urllib.request

payload = {
    "name": "Advisor onboarding call",
    "signalType": "transcript",
    "text": "Customer interview transcript goes here.",
    "additionalContext": "Imported from an onboarding call.",
    "sourceLinks": [
        {
            "name": "Open source call",
            "url": "https://meetings.example.com/calls/advisor-onboarding-call",
            "type": "transcript",
            "primary": True,
        }
    ],
}

def request_json(method, path, body=None):
    request = urllib.request.Request(
        f"{os.environ['ZENTRIK_API_BASE'].rstrip('/')}{path}",
        data=json.dumps(body).encode("utf-8") if body is not None else None,
        method=method,
        headers={
            "Authorization": f"Bearer {os.environ['ZENTRIK_API_KEY']}",
            "Content-Type": "application/json",
            "Accept": "application/json",
        },
    )
    with urllib.request.urlopen(request, timeout=60) as response:
        raw = response.read().decode("utf-8")
        return response.status, json.loads(raw) if raw else None

code, created = request_json("POST", "/external/v1/signals", payload)
public_id = created["publicId"]
print(f"Created {public_id} jobId={created['jobId']}")

for _ in range(36):
    code, signal = request_json("GET", f"/external/v1/signals/{public_id}")
    print(f"{public_id} status={signal.get('status')} insights={len(signal.get('insightIds') or [])}")
    if signal.get("status") in {"processed", "failed"}:
        print(json.dumps(signal, indent=2))
        break
    time.sleep(5)

Expected result:

  • the create response returns a publicId and jobId
  • the signal moves from processing to processed
  • the signal appears in Discovery with linked insights when processing succeeds
  • the source link appears on Signal and insight evidence surfaces so reviewers can open the original record

Choose a signal type

Use the signal type that matches the source material:

  • transcript for interviews, onboarding calls, sales calls, and meeting notes with direct user evidence
  • support_ticket for support cases, ticket exports, or issue threads
  • review for public reviews, app-store reviews, or shorter rating-backed comments
  • feedback_record for survey rows, NPS verbatims, CSV feedback, or form responses
  • discovery_report for synthesized research notes or analyst writeups

If you are unsure, start with the source that is closest to the original customer wording instead of a summarized derivative.

Whenever the source has a stable final URL, include sourceLinks. Use the primary link for the place a reviewer should land first: the exact review, ticket, Reddit comment, report, meeting, source row, or source-system record. Omit sourceLinks only when the provider has no durable user-facing URL.

For recurring imports, also send a stable externalId, source-date occurredAt, and canonical source fields (providerType, evidenceKind, analysisProfile) so filtering, source labels, and processing remain predictable as volume grows.

After you create a signal

The create endpoint queues processing automatically. You do not need to call a second endpoint for the first run.

The main fields to watch are:

  • status
  • insightIds
  • processing.lastJobId
  • processing.processedAt
  • processing.lastError

Use the explicit process endpoint only when you want to rerun an existing signal.

Next steps

When the Hello World flow works, move to one of these:

Troubleshooting

Check these steps against what you see in your workspace. If something differs, note your workspace name and the screen, then contact us.

The create call fails immediately

Check the API key, the base URL, and whether the base URL includes /api. A valid create response returns both a signal identifier and a queued job id.

The signal stays in processing

Check the signal record for processing.lastError. Correct the reported problem before retrying, and contact support if the signal remains in processing without an error.

Continue from here

Did this guide answer your question?

Your response helps us prioritize missing or unclear documentation.

Still stuck?

Send your question to Zentrik support. This guide will be included automatically.

Ask Zentrik support

Meet Zentrik Loops on Product Hunt.

See the launch