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.
#!/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:
- Signals import template for batching, retries, polling, and richer payload mapping
- Signals API reference for the full endpoint contract
- Import evidence into Discovery if you need the product-level decision guide
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
Related guides
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.