Skip to documentation

Signals

Signals are the imported transcripts, tickets, reviews, and reports that feed Discovery. Use these endpoints when you want to bring evidence into Zentrik from another source.

6 endpoints

GET

List signals

GET /external/v1/signals

List a bounded page of signals in your workspace. Signals are imported pieces of customer or market evidence such as transcripts, support tickets, reviews, and discovery reports.

Requirements

API scopes required:
signals:read

Request

Parameters

NameTypeDescription
limit numberMaximum number of items to return (default: 20, maximum: 100)
offset numberNumber of items to skip (default: 0)
include stringComma-separated extras. 'raw' adds rawData: the whole stored data column, including every attributed transcript turn and the provider's import response. Opt-in because it can exceed several hundred KB per signal.

Responses

200
Success

Signals were successfully retrieved. Pagination metadata is returned in X-Total-Count, X-Limit, X-Offset, and X-Has-More headers.

Schema
Array<Signal>

Example Request

GET
/external/v1/signals
curl
curl -X GET https://zentrik.ai/api/external/v1/signals?limit=10 \
  -H 'Authorization: Bearer YOUR_API_KEY'

Example Response

200 OK
json
[
  {
    "id": "uuid",
    "name": "Support Ticket - 3/20/24, 10:00 AM",
    "publicId": "SIGNAL-42",
    "workspaceId": "workspace-uuid",
    "status": "processed",
    "context": "support_request",
    "signalType": null,
    "processorType": "zendesk",
    "providerType": "zendesk",
    "evidenceKind": "support_ticket",
    "analysisProfile": "support_ticket",
    "provenance": {
      "providerType": "zendesk",
      "evidenceKind": "support_ticket",
      "analysisProfile": "support_ticket"
    },
    "sourceFacet": "zendesk:support_ticket",
    "sourceLabel": "Zendesk support ticket",
    "sourceLinks": [
      {
        "name": "Open Zendesk ticket",
        "url": "https://example.zendesk.com/agent/tickets/12345",
        "type": "support_ticket",
        "primary": true
      }
    ],
    "source": {
      "type": "zendesk",
      "external_data": {
        "ticketId": "12345"
      }
    },
    "data": {
      "subject": "Users cannot reset password",
      "severity": "high"
    },
    "transcript": null,
    "import": null,
    "insightIds": [],
    "accountIds": [],
    "accountId": null,
    "accountExternalId": null,
    "externalId": "external-123",
    "createdAt": "2024-03-20T10:00:00Z",
    "updatedAt": "2024-03-20T10:00:00Z"
  }
]
GET

Get one signal

GET /external/v1/signals/:publicId

Retrieve a single signal by its public ID.

Requirements

API scopes required:
signals:read

Request

Parameters

NameTypeDescription
publicId *stringWorkspace-scoped public ID, such as SIGNAL-42
include stringComma-separated extras. 'raw' adds rawData: the whole stored data column. Opt-in because a processed transcript's column can exceed several hundred KB.

Responses

200
Success

The signal was successfully retrieved.

Schema
404
Not Found

No signal found with the provided ID.

Example Request

GET
/external/v1/signals/:publicId
curl
curl -X GET https://zentrik.ai/api/external/v1/signals/SIGNAL-42 \
  -H 'Authorization: Bearer YOUR_API_KEY'

Example Response

200 OK
json
{
  "id": "uuid",
  "name": "Support Ticket - 3/20/24, 10:00 AM",
  "publicId": "SIGNAL-42",
  "workspaceId": "workspace-uuid",
  "status": "processed",
  "context": "support_request",
  "signalType": null,
  "processorType": "zendesk",
  "providerType": "zendesk",
  "evidenceKind": "support_ticket",
  "analysisProfile": "support_ticket",
  "provenance": {
    "providerType": "zendesk",
    "evidenceKind": "support_ticket",
    "analysisProfile": "support_ticket"
  },
  "sourceFacet": "zendesk:support_ticket",
  "sourceLabel": "Zendesk support ticket",
  "sourceLinks": [
    {
      "name": "Open Zendesk ticket",
      "url": "https://example.zendesk.com/agent/tickets/12345",
      "type": "support_ticket",
      "primary": true
    }
  ],
  "source": {
    "type": "zendesk",
    "external_data": {
      "ticketId": "12345"
    }
  },
  "data": {
    "sessionId": "session-123"
  },
  "transcript": null,
  "import": null,
  "insightIds": [
    "insight-uuid"
  ],
  "accountIds": [],
  "accountId": null,
  "accountExternalId": null,
  "externalId": "external-123",
  "createdAt": "2024-03-20T10:00:00Z",
  "updatedAt": "2024-03-20T10:00:00Z"
}
POST

Create a signal

POST /external/v1/signals

Create a new signal by sending the text you want Zentrik to analyze. This is the recommended public API flow for transcripts, support tickets, reviews, structured feedback records, and discovery reports. Advanced raw signal bodies are also accepted on this same endpoint and are auto-queued.

Requirements

API scopes required:
signals:write

Request

Request body (application/json)

namestring

Optional display name for the signal. When omitted for typed text signals, Zentrik generates one from the signal type and timestamp.

textstring
Required

The raw transcript, support ticket, review, structured feedback record, or discovery report text that Zentrik should process into insights. Transcript input may be copied or exported with speaker labels, timestamps, captions, markdown headings, and common meeting metadata; Zentrik preserves the source and normalizes its conversation structure automatically.

signalType'transcript' | 'support_ticket' | 'review' | 'feedback_record' | 'discovery_report'
Required

What kind of text you are sending. This determines how Zentrik processes the signal. New text signals are automatically queued for processing after creation.

providerTypestring

Optional source system or channel identifier, such as app_store, reddit, g2, gartner, manual_csv, or external_api.

evidenceKindstring

Optional evidence shape override. For typed text this defaults from signalType and usually does not need to be sent.

analysisProfile'default_transcript' | 'support_ticket' | 'survey_response' | 'discovery_report' | 'community_discussion'

Optional processing profile override. For typed text this defaults from signalType.

additionalContextstring

Optional notes to help Zentrik interpret the text correctly. Use this for things like where the text came from, what batch it belongs to, or any lightweight analyst note. This is treated as supplemental context, not an instruction override.

occurredAtiso-date

Optional timestamp representing when the original signal happened. When provided, it is used as the signal creation timestamp for trend accuracy.

productIdsuuid[]

Optional list of products to narrow the product context used during processing.

accountIduuid

Optional account to associate with this signal and the insights created from typed text processing.

accountIdsuuid[]

Optional list of account IDs to associate directly with this signal. Use accountId for the common single-account case.

accountExternalIdstring

Optional stable client-side account identifier, such as crm-example-account. Prefer this for CRM automations that should not persist backend UUIDs.

participants{ email?: string; contactId?: uuid; accountId?: uuid; name?: string; role?: string; affiliation?: "external" | "internal" | "unknown" }[]

People observed in this record. Add a valid email or an existing Contact ID. Mark colleagues as internal. Zentrik changes People only when participantPolicy requests it.

participantPolicy'none' | 'match_existing' | 'create_missing'

Controls participant handling. Omit it or use none for no People changes. Use match_existing to link only saved People, or create_missing to create reliably identified People.

createMissingAccountsboolean

When participantPolicy is create_missing, also allow one missing Account to be created from each unambiguous professional email domain. Defaults to false; public email domains never create Accounts.

sourceKeystring

Stable namespace for externalId, such as granola.note or crm.call. Set it when several clients send records into the same workspace.

externalIdstring

Stable record identifier within sourceKey. Later deliveries reuse the Signal and can add newly available participant links. Use Idempotency-Key only to replay one exact request.

sourceLinks{ name?: string; url: string; type?: string; primary?: boolean }[]

Links users should land on from Zentrik, such as the original app store review, G2 review, Reddit thread or comment, Gartner report, support ticket, source row, or meeting link. HTTP and HTTPS URLs are normalized and returned on the Signal object.

sourceExternalDataobject

Provider-specific metadata to keep with the signal, such as reviewId, rating, locale, reportId, threadId, productSlug, or dataset. For Granola, send noteId for a public note ID or granolaDocumentId for a legacy desktop ID; send both when known. Zentrik stores this under source.external_data.

Responses

201
Created

The signal was successfully created and queued for asynchronous processing. Create responses include a queued job id.

Schema

Example Request

POST
/external/v1/signals
json
{
  "name": "G2 Reviews - March Batch",
  "text": "Users love the product overall, but several reviewers mention that exports are hard to find and slow to complete for large datasets.",
  "signalType": "review",
  "providerType": "g2",
  "externalId": "g2-review:review-row-1",
  "additionalContext": "Public review batch collected from G2 and Capterra.",
  "occurredAt": "2025-03-11T14:30:00Z",
  "productIds": [
    "product-uuid-1"
  ],
  "participants": [
    {
      "email": "buyer@example.com",
      "name": "Primary product contact",
      "role": "VP Product",
      "affiliation": "external"
    }
  ],
  "participantPolicy": "create_missing",
  "createMissingAccounts": false,
  "sourceExternalData": {
    "reviewId": "review-row-1",
    "rating": 3,
    "productSlug": "example-product"
  },
  "sourceLinks": [
    {
      "name": "Open G2 review",
      "url": "https://www.g2.com/products/example-product/reviews/review-row-1",
      "type": "review",
      "primary": true
    }
  ]
}

Example Response

201 Created
json
{
  "id": "uuid",
  "name": "Review - 3/11/25, 2:30 PM",
  "publicId": "SIGNAL-43",
  "workspaceId": "workspace-uuid",
  "status": "processing",
  "context": null,
  "signalType": "review",
  "processorType": "manual-text",
  "providerType": "g2",
  "evidenceKind": "review",
  "analysisProfile": "survey_response",
  "provenance": {
    "providerType": "g2",
    "evidenceKind": "review",
    "analysisProfile": "survey_response"
  },
  "sourceFacet": "g2:review",
  "sourceLabel": "G2 Review",
  "sourceLinks": [
    {
      "name": "Open G2 review",
      "url": "https://www.g2.com/products/example-product/reviews/review-row-1",
      "type": "review",
      "primary": true
    }
  ],
  "source": {
    "type": "manual-text",
    "external_data": {
      "reviewId": "review-row-1",
      "rating": 3,
      "productSlug": "example-product",
      "links": [
        {
          "name": "Open G2 review",
          "url": "https://www.g2.com/products/example-product/reviews/review-row-1",
          "type": "review",
          "primary": true
        }
      ]
    }
  },
  "data": {
    "s3Key": "signals/manual-text/workspace-uuid/uuid.txt",
    "productIds": [
      "product-uuid-1"
    ],
    "accountId": "account-uuid-1",
    "accountExternalId": "crm-example-account",
    "additionalContext": "Public review batch collected from G2 and Capterra.",
    "uploadedAt": "2025-03-21T10:00:00Z"
  },
  "insightIds": [],
  "accountId": "account-uuid-1",
  "accountIds": [
    "account-uuid-1"
  ],
  "contactIds": [
    "contact-uuid-1"
  ],
  "externalId": "g2-review:review-row-1",
  "createdAt": "2025-03-11T14:30:00Z",
  "updatedAt": "2025-03-21T10:00:00Z",
  "jobId": "job-123",
  "processing": {
    "lastJobId": "job-123",
    "startedAt": "2025-03-21T10:00:00Z",
    "processedAt": null,
    "failedAt": null,
    "lastError": null
  }
}
PATCH

Update a signal

PATCH /external/v1/signals/:publicId

Update fields of an existing signal, such as metadata or linked insights.

Requirements

API scopes required:
signals:write

Request

Parameters

NameTypeDescription
publicId *stringWorkspace-scoped public ID, such as SIGNAL-43

Request body (application/json)

sourceobject

Updated source descriptor for the signal.

dataobject

Updated arbitrary context payload.

insightIdsstring[]

Replace the list of linked insight IDs.

externalIdstring

Updated external record identifier.

status'pending' | 'processed'

Processing status for the signal.

contextstring

Persisted signal context, such as user_interview or support_request.

Responses

200
Updated

The signal was successfully updated.

Schema

Example Request

PATCH
/external/v1/signals/:publicId
curl
curl -X PATCH https://zentrik.ai/api/external/v1/signals/SIGNAL-43 \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"insightIds": ["insight-uuid"]}'
DELETE

Delete a signal

DELETE /external/v1/signals/:publicId

Permanently delete a signal from the workspace.

Requirements

API scopes required:
signals:delete

Request

Parameters

NameTypeDescription
publicId *stringWorkspace-scoped public ID, such as SIGNAL-43

Responses

200
Success

The signal was successfully deleted.

Schema
{ message: string }

Example Request

DELETE
/external/v1/signals/:publicId
curl
curl -X DELETE https://zentrik.ai/api/external/v1/signals/SIGNAL-43 \
  -H 'Authorization: Bearer YOUR_API_KEY'

Example Response

200 OK
json
{
  "message": "Deleted"
}
POST

Process a signal

POST /external/v1/signals/:publicId/process

Queue signal processing for an existing signal. Use this for explicit re-runs or manual retries.

Requirements

API scopes required:
signals:write

Request

Parameters

NameTypeDescription
publicId *stringWorkspace-scoped public ID, such as SIGNAL-43.

Responses

201
Queued

The signal was queued for asynchronous processing.

Schema
{ jobId: string }

Example Request

POST
/external/v1/signals/:publicId/process
curl
curl -X POST https://zentrik.ai/api/external/v1/signals/SIGNAL-43/process \
  -H 'Authorization: Bearer YOUR_API_KEY'

Example Response

201 Created
json
{
  "jobId": "job-456"
}

Creation Modes

The recommended public API flow is typed text ingestion. Sendtext andsignalTypeto create a manual signal that is automatically queued for AI processing into insights.

Supported signal types: transcript, support_ticket, review, feedback_record, discovery_report

Transcript normalization: copied text, Markdown, speaker labels, timestamps, SRT, and WebVTT are accepted without a connected meeting provider. Known meeting headers become metadata; ambiguous text remains unresolved rather than receiving an invented speaker.

Canonical provenance: processorType controls routing, providerType identifies the source system, evidenceKind describes the evidence shape, and analysisProfile selects the extraction behavior.

Source links: send sourceLinks with final URLs for the original review, thread, report, ticket, source row, or meeting so Zentrik can render direct evidence actions in Discovery.

Profile mapping: transcript → transcript analysis, support_ticket → ticket analysis, review and feedback_record → feedback/review analysis, discovery_report → synthesized discovery-report analysis

Async behavior: create returns a processing signal plus a jobId

Inferred context: you do not need to send a context label. Zentrik infers the best matching interaction context during processing.

Advanced/raw ingestion: the same create endpoint also supports lower-level fields such as processorType, providerType, evidenceKind, source, and data.

Those fields are intentionally omitted from the primary docs here because most public API users only need the typed text flow.

Data Models

Signals are intentionally flexible. The core fields help you track provenance while thesource anddata payloads carry your custom structure.

Signal Object

FieldTypeDescription
iduuidThe unique identifier for the signal.
namestringDisplay name for the signal. For typed text signals, Zentrik can generate this automatically from the signal type and timestamp.
publicIdstringWorkspace-scoped, human-readable identifier in the form "SIGNAL-123". Useful for referencing signals in UI and exports.
workspaceIduuidID of the workspace this signal belongs to.
status'pending' | 'processing' | 'processed' | 'failed'Processing status of the signal. Create responses that queue work now typically return processing immediately, then later settle to processed or failed.
signalType'transcript' | 'support_ticket' | 'review' | 'feedback_record' | 'discovery_report' | nullPresent for manual text signals. Indicates which typed ingestion flow created the signal.
processorTypestringCanonical operational processor route, such as manual-text, manual-transcript, gong, or zendesk.
providerTypestringCanonical source system or channel, such as external_api, manual, gong, zendesk, gmail_mailbox, app_store, reddit, or g2.
evidenceKindstringCanonical evidence shape, such as transcript, support_ticket, review, feedback_record, forum_discussion, discovery_report, or email_thread.
analysisProfile'default_transcript' | 'support_ticket' | 'survey_response' | 'discovery_report' | 'community_discussion'Canonical prompt/extraction profile used by processing.
provenance{ providerType: string; evidenceKind: string; analysisProfile: string }Canonical signal provenance grouped for clients that do not need operational processor routing.
sourceFacetstringDerived filter/grouping key in the form providerType:evidenceKind.
sourceLabelstringDerived display label for the source facet.
sourceLinks{ name: string; url: string; type: string; primary?: boolean }[]Normalized source links users can open from Discovery to inspect the original evidence. These are derived from sourceLinks on create and legacy URL fields stored in source.external_data, data, or import metadata.
contextstring | nullNormalized interaction context inferred or stored for the signal, such as user_interview, feature_feedback, or support_request. Most clients do not need to send this on create.
source{ type: string; external_data?: object }Provider-specific source payload. Canonical provenance lives in processorType, providerType, evidenceKind, and analysisProfile.
dataobject | nullThe source-shaped payload you sent on create, and only that. Keys Zentrik writes into the stored column while processing (attribution, import receipts, storage keys, processing timestamps) are projected into transcript, import, and processing instead of echoed here. Null when you sent none.
transcript{ coveragePercent, totalTurnCount, resolvedTurnCount, externalTurnCount, internalTurnCount, unresolvedTurnCount, participantCount, analysis } | nullFor transcript evidence: how much of the call was attributed to a named speaker, and what processing decided to do with it. Null for other evidence kinds.
transcript.analysis{ outcome: 'no_analytical_content' | 'analyzed_no_insights' | 'analyzed_with_insights'; reason: string; generatedInsightCount: number } | nullWhy a processed call did or did not produce insights. no_analytical_content means the call never reached insight analysis; analyzed_no_insights means it was analyzed in full and nothing in it was supported well enough to become an insight; analyzed_with_insights means it produced them. generatedInsightCount counts what that processing run produced rather than the length of insightIds, so a call can report analyzed_no_insights and still list insights a person linked by hand. Null until the signal has been processed, so a call with no insights and no analysis is still in flight rather than declined.
importobject | nullScalar provenance facts from the import that created this signal, such as provider, filename, sheet, row, and record IDs. Null when the signal was not imported.
rawDataobject | nullThe whole stored data column, returned only when a read request includes ?include=raw. A processed transcript carries every attributed turn and the provider import response and can exceed several hundred KB, so it is opt-in per request and never returned on a list.
insightIdsuuid[]List of insight IDs that have been linked to this signal.
accountIdsuuid[]List of account IDs linked directly to this signal.
contactIdsuuid[]Workspace people linked to this signal from participant resolution or later curation.
accountIduuid | nullLegacy single-account field. When multiple accounts are linked, this returns the first linked account ID.
accountExternalIdstring | nullClient-provided external account identifier when the signal was created with accountExternalId.
externalIdstring | nullIdentifier of the record in the external system (for example, a ticket or event identifier).
createdAtiso-dateTimestamp when the signal was first created.
updatedAtiso-dateTimestamp of the last modification.
jobIdstring | undefinedReturned on create/process responses when an asynchronous processing job has been queued.
processing{ lastJobId?: string | null; startedAt?: string | null; processedAt?: string | null; failedAt?: string | null; lastError?: string | null } | nullOperational processing metadata. Use this to debug queue progress and retries before assuming a signal is stuck.