Creating a Signal

Create a company signal synchronously

POST
/v1/companies/signals/sync

⭐ This is the recommended way to create signals and get results instead of async + webhooks.

Creates a signal and waits synchronously for it to complete, returning the result in the same request. This endpoint blocks until the signal processing finishes or a timeout is reached.


Why Use the Sync Endpoint?

Advantages over async + webhooks:

  • ✅ Get results immediately in one request
  • ✅ No webhook hosting required
  • ✅ No polling implementation needed
  • ✅ Simpler integration
  • ✅ Better for synchronous workflows

How It Works

  1. Send your signal creation request (same payload as regular POST /v1/companies/signals)
  2. The endpoint creates the signal and polls for completion
  3. Returns the completed signal with results when processing finishes
  4. If timeout is reached, returns the latest status (processing)

Timeout Configuration

  • Default timeout: Configured per API key
  • Max timeout: 900 seconds (15 minutes)
  • Custom timeout: Use X-Sbr-Timeout-Sec header to set custom timeout (up to max)
  • Error on timeout: Use X-Sbr-Timeout-Error: true header to return 408 on timeout instead of latest status

Response Codes

  • 200 OK - Signal completed successfully
  • 202 Accepted - Timeout reached, signal still processing
  • 408 Request Timeout - Timeout reached (when X-Sbr-Timeout-Error: true)

Using with Templates

Use signalTemplateId instead of defining questions inline for standardized research:

{  "domain": "acme.com",  "signalTemplateId": "a12b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"}

Templates let you reuse research questions, qualification criteria, and answer types across multiple companies. Create templates via POST /v1/companies/signals/templates.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.

In: header

Header Parameters

X-Sbr-Timeout-Sec?integer

Custom timeout in seconds (max 900). Defaults to your API key's configured timeout.

Range1 <= value <= 900
X-Sbr-Timeout-Error?string

If "true", returns 408 Request Timeout on timeout instead of 202 with latest status

Value in"true" | "false"

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

domain*string

The company domain to research (e.g., "acme.com")

Match^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$
Length1 <= length <= 253
question*string

The research question to ask about the company

Length1 <= length <= 500
answerType?string

The expected format of the answer

Default"open_text"
Value in"open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"
outputSchema?

JSON Schema defining the expected output structure. Required when answerType is "json_schema". The AI will generate data conforming to this schema.

Supported JSON Schema features:

  • type: object, array, string, number, integer, boolean, null
  • properties: Define object properties
  • required: List of required property names
  • items: Schema for array items
  • enum: Enumeration of allowed values
  • description: Property descriptions (helps AI generate better data)

Limitations:

  • Maximum 100 total properties
  • Maximum 5 levels of nesting
  • anyOf, oneOf, allOf are not supported
  • $ref only supports self-references for recursive schemas
webhookUrl?string

Optional webhook URL to receive notifications when processing completes. Webhooks are only sent for freshly processed signals — cached results do not trigger webhooks. Set forceRefresh: true to bypass the cache and ensure webhook delivery.

Formaturi
Lengthlength <= 2048
weight?string

The importance/weight of the signal

Value in"important" | "nice_to_have" | "not_important"
qualificationCriteria?

Qualification criteria mapping answer values based on answerType. The structure must match the answerType specified.

For boolean answerType:

{  "yes": "good",  "no": "disqualified"}

For number, percentage, or currency answerType:

{  "ranges": [    { "rangeStart": 0, "rangeEnd": 100, "answerValue": "neutral" },    { "rangeStart": 101, "rangeEnd": 1000, "answerValue": "good" }  ]}

For list answerType:

{  "choices": {    "salesforce": { "answerValue": "good", "label": "Salesforce" },    "hubspot": { "answerValue": "neutral", "label": "HubSpot" },    "none": { "answerValue": "disqualified", "label": "No CRM" }  }}

Valid answerValue options (from worst to best):

  • disqualified: Answer indicates a deal-breaker or disqualifying factor
  • poor: Answer indicates a weak fit
  • neutral: Answer is acceptable but not ideal
  • good: Answer indicates a strong fit
  • excellent: Answer indicates an exceptional fit
signalTemplateId?string

Optional signal template ID to use. When provided, the template configuration (question, answerType, weight, qualificationCriteria) will be used. The system always resolves to the latest active version of the template. Only the domain parameter is required when using a template.

Formatuuid
forceRefresh?boolean

Force re-run of the signal analysis, skipping the cache. When set to true, a new signal will be created even if a cached result exists.

Defaultfalse
connectors?

Connector configuration for signal generation

verificationMode?string

Controls how strictly Saber verifies answers before responding.

  • strict: Only returns answers backed by verified sources (primary or trusted secondary sources). Returns null when information is unavailable.
  • lenient: Allows logical inference and best-effort estimates when direct evidence is missing. Uses industry benchmarks, logical correlates, and quantitative metrics.
Default"strict"
Value in"strict" | "lenient"

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/companies/signals/sync" \  -H "X-Sbr-Timeout-Sec: 120" \  -H "X-Sbr-Timeout-Error: false" \  -H "Content-Type: application/json" \  -d '{    "domain": "clay.com",    "question": "Does this company have open roles in operations?",    "answerType": "boolean"  }'

{  "id": "e45c1dc4-d422-4b51-956b-cb6d3ddaf250",  "status": "completed",  "domain": "clay.com",  "question": "Does this company have open roles in operations?",  "createdAt": "2024-01-15T10:30:00Z",  "completedAt": "2024-01-15T10:32:15Z",  "answer": {    "type": "boolean",    "boolean": {      "value": true    }  },  "reasoning": "Based on their careers page, Clay has multiple open positions in Operations including Operations Manager and Operations Coordinator roles",  "confidence": 0.95,  "sources": [    {      "url": "https://clay.com/careers",      "title": "Careers at Clay",      "snippet": "We're hiring for Operations Manager and Operations Coordinator roles..."    }  ]}

{  "id": "b78e4gd7-g755-6e84-c89e-fe9f6ggch583",  "status": "processing",  "domain": "clay.com",  "question": "What industry does this company serve?",  "createdAt": "2024-01-15T10:30:00Z"}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}

Create a company signal asynchronously

POST
/v1/companies/signals

Submit a domain and question to create a new research signal. The AI will analyze the domain and provide a structured answer based on the specified answer type.


When to Use This Endpoint

Use this endpoint for event-driven architectures where you need webhook notifications. For simpler integrations, use the /v1/companies/signals/sync endpoint (recommended) which returns results immediately.


Webhooks

Configure webhooks by including a webhookUrl parameter in your request.

Webhook events:

  • signal.completed - Signal processing finished successfully
  • signal.failed - Signal processing failed

Webhook security: All webhooks include HMAC-SHA256 signatures in the X-Webhook-Signature header for verification.

Webhook reliability:

  • Failed deliveries are retried up to 25 times with exponential backoff
  • Webhook requests timeout after 30 seconds
  • Your endpoint should respond with 2xx status code

Caching Behavior

Your response will be cached for up to 12 hours by default, unless you set the forceRefresh flag to true.

Cache is based on a hash of the following fields: domain (or contactProfileUrl for contact signals), question, answer type, verification mode, and output schema (if provided). Changing any of these fields will create a new signal. We will not send you the webhook if you hit the cache, even if the webhook URL in the payload is different from the original request.

Instead you will receive the latest status of the signal immediately in the response.


Using with Templates

Use signalTemplateId instead of defining questions inline for standardized research:

{  "domain": "acme.com",  "signalTemplateId": "a12b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",  "webhookUrl": "https://myapp.com/webhooks/signals"}

Templates let you reuse research questions, qualification criteria, and answer types across multiple companies. Create templates via POST /v1/companies/signals/templates.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

domain*string

The company domain to research (e.g., "acme.com")

Match^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$
Length1 <= length <= 253
question*string

The research question to ask about the company

Length1 <= length <= 500
answerType?string

The expected format of the answer

Default"open_text"
Value in"open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"
outputSchema?

JSON Schema defining the expected output structure. Required when answerType is "json_schema". The AI will generate data conforming to this schema.

Supported JSON Schema features:

  • type: object, array, string, number, integer, boolean, null
  • properties: Define object properties
  • required: List of required property names
  • items: Schema for array items
  • enum: Enumeration of allowed values
  • description: Property descriptions (helps AI generate better data)

Limitations:

  • Maximum 100 total properties
  • Maximum 5 levels of nesting
  • anyOf, oneOf, allOf are not supported
  • $ref only supports self-references for recursive schemas
webhookUrl?string

Optional webhook URL to receive notifications when processing completes. Webhooks are only sent for freshly processed signals — cached results do not trigger webhooks. Set forceRefresh: true to bypass the cache and ensure webhook delivery.

Formaturi
Lengthlength <= 2048
weight?string

The importance/weight of the signal

Value in"important" | "nice_to_have" | "not_important"
qualificationCriteria?

Qualification criteria mapping answer values based on answerType. The structure must match the answerType specified.

For boolean answerType:

{  "yes": "good",  "no": "disqualified"}

For number, percentage, or currency answerType:

{  "ranges": [    { "rangeStart": 0, "rangeEnd": 100, "answerValue": "neutral" },    { "rangeStart": 101, "rangeEnd": 1000, "answerValue": "good" }  ]}

For list answerType:

{  "choices": {    "salesforce": { "answerValue": "good", "label": "Salesforce" },    "hubspot": { "answerValue": "neutral", "label": "HubSpot" },    "none": { "answerValue": "disqualified", "label": "No CRM" }  }}

Valid answerValue options (from worst to best):

  • disqualified: Answer indicates a deal-breaker or disqualifying factor
  • poor: Answer indicates a weak fit
  • neutral: Answer is acceptable but not ideal
  • good: Answer indicates a strong fit
  • excellent: Answer indicates an exceptional fit
signalTemplateId?string

Optional signal template ID to use. When provided, the template configuration (question, answerType, weight, qualificationCriteria) will be used. The system always resolves to the latest active version of the template. Only the domain parameter is required when using a template.

Formatuuid
forceRefresh?boolean

Force re-run of the signal analysis, skipping the cache. When set to true, a new signal will be created even if a cached result exists.

Defaultfalse
connectors?

Connector configuration for signal generation

verificationMode?string

Controls how strictly Saber verifies answers before responding.

  • strict: Only returns answers backed by verified sources (primary or trusted secondary sources). Returns null when information is unavailable.
  • lenient: Allows logical inference and best-effort estimates when direct evidence is missing. Uses industry benchmarks, logical correlates, and quantitative metrics.
Default"strict"
Value in"strict" | "lenient"

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/companies/signals" \  -H "Content-Type: application/json" \  -d '{    "domain": "clay.com",    "question": "What is Clay\'s main product offering?",    "answerType": "open_text",    "webhookUrl": "https://myapp.com/webhooks/signals"  }'
{  "id": "e45c1dc4-d422-4b51-956b-cb6d3ddaf250",  "status": "processing",  "domain": "clay.com",  "question": "What is Clay's main product offering?",  "createdAt": "2024-01-15T10:30:00Z"}
{  "error": "BAD_REQUEST",  "message": "Invalid JSON in request body"}
{  "error": "UNAUTHORIZED",  "message": "Missing or invalid API key"}
{  "error": "VALIDATION_ERROR",  "message": "Invalid request body",  "details": [    {      "field": "domain",      "message": "must be a valid hostname"    }  ]}
{  "statusCode": 429,  "message": "Rate limit exceeded",  "error": "Too Many Requests",  "retryAfter": 45}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}

Create company signals in batch

POST
/v1/companies/signals/batch

Submit multiple signals and domains to create signals using a Cartesian product pattern. Each signal is combined with each domain to create individual signals.

Two ways to create batch signals:

  1. Inline Questions: Define questions directly in the request for ad-hoc research without creating templates
  2. Using Templates (recommended for reusability): Reference pre-created templates by ID for consistent, reusable questions

Cartesian Product Logic:

  • If you submit 2 templates and 3 domains, the system creates 6 signals (2 × 3)
  • Sync mode (default): Maximum 100 resulting signals (templates × domains ≤ 100)
  • Async mode (async: true): Maximum 20,000 resulting signals (templates × domains ≤ 20,000)

Sync Mode (default):

  • Returns inline results with individual signal statuses
  • Maximum 100 signals per batch
  • Returns 202 with results[] array

Async Mode (async: true):

  • For large batches up to 20,000 signals
  • Returns immediately with a batchId for tracking
  • Signals are processed in background chunks
  • Returns 202 with batchId, totalSignals, status: "accepted"
  • Poll /v1/companies/signals/batches/status?domain=... for progress

Partial Failure Handling (sync mode):

  • Individual signal failures don't block other signals from processing
  • Each signal in the response includes its own status (accepted or failed)
  • Returns 202 if at least one signal is scheduled successfully
  • Returns 422 if all signals failed to be scheduled

Processing:

  • All signals are processed asynchronously
  • No guarantee of processing order
  • Use the /v1/companies/signals/sync endpoint (recommended) to retrieve results as they complete

Webhooks:

  • Each signal in the batch can include a webhookUrl to receive notifications on completion
  • Webhooks fire individually per signal as each one completes processing
  • Webhook events: signal.completed (success) or signal.failed (failure)
  • All webhooks include HMAC-SHA256 signatures in the X-Webhook-Signature header

Caching Behavior:

  • Signals are cached based on a hash of domain, question, answer type, verification mode, and output schema (if provided)
  • If a matching cached result exists, the cached response is returned immediately and no webhook is sent
  • To force fresh processing (and webhook delivery), set forceRefresh: true on individual signals

Automatic Summary Generation:

  • Set generateSummaryOnComplete to true to automatically generate summaries when all signals in the batch complete
  • Summaries are generated per domain (one summary per unique domain in the batch)
  • Summary generation is triggered automatically once all signals for a domain have completed
  • Use the /v1/companies/signals/summaries endpoint to retrieve generated summaries

Authorization

ApiKeyAuth
AuthorizationBearer <token>

API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

signals*array<>

Array of signals to create. Each signal will be combined with each domain using Cartesian product logic. You can either:

  • Provide inline signal configuration with question and optional fields (quick ad-hoc research)
  • Use templateId to reference a pre-created template (recommended for reusability)
Items1 <= items
domains*array<>

Array of company domains to research. Each domain will be combined with each signal template using Cartesian product logic. The total resulting signals (len(signals) × len(domains)) must not exceed 100.

Items1 <= items <= 100
generateSummaryOnComplete?boolean

Whether to automatically generate a summary when all signals in the batch are completed. When set to true, a summary will be automatically generated for each unique domain in the batch once all signals for that domain have completed processing.

Defaultfalse
async?boolean

When true, enables async processing with a higher limit of 20,000 signals. Returns a batchId for tracking instead of inline results. The response shape differs from sync mode — returns { batchId, submittedAt, totalSignals, status, async }. Use /v1/companies/signals/batches/status?domain=... to poll for progress.

Defaultfalse

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/companies/signals/batch" \  -H "Content-Type: application/json" \  -d '{    "signals": [      {        "question": "What is their main product offering?",        "answerType": "open_text",        "weight": "important"      },      {        "question": "How many employees do they have?",        "answerType": "number",        "weight": "nice_to_have"      },      {        "question": "Are they venture-backed?",        "answerType": "boolean",        "weight": "important"      }    ],    "domains": [      "acme.com",      "techcorp.io"    ],    "generateSummaryOnComplete": false  }'

{  "batchId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",  "submittedAt": "2024-01-15T10:30:00Z",  "totalSignals": 15000,  "status": "accepted",  "async": true}

{  "error": "BAD_REQUEST",  "message": "Invalid JSON in request body"}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}

{  "error": "VALIDATION_ERROR",  "message": "Invalid request body",  "details": [    {      "field": "domains",      "message": "at least one domain is required"    }  ]}

{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}

Create a contact signal synchronously

POST
/v1/contacts/signals/sync

⭐ This is the recommended way to create contact signals and get results instead of async + webhooks.

Creates a contact signal and waits synchronously for it to complete, returning the result in the same request. Works identically to the company signals sync endpoint but for contact-based signals.


Why Use the Sync Endpoint?

Advantages over async + webhooks:

  • ✅ Get results immediately in one request
  • ✅ No webhook hosting required
  • ✅ No polling implementation needed
  • ✅ Simpler integration
  • ✅ Better for synchronous workflows

How It Works

  1. Send your signal creation request (same payload as regular POST /v1/contacts/signals)
  2. The endpoint creates the signal and polls for completion
  3. Returns the completed signal with results when processing finishes
  4. If timeout is reached, returns the latest status (processing)

Timeout Configuration

  • Default timeout: Configured per API key
  • Max timeout: 900 seconds (15 minutes)
  • Custom timeout: Use X-Sbr-Timeout-Sec header to set custom timeout (up to max)
  • Error on timeout: Use X-Sbr-Timeout-Error: true header to return 408 on timeout instead of latest status

Response Codes

  • 200 OK - Signal completed successfully
  • 202 Accepted - Timeout reached, signal still processing
  • 408 Request Timeout - Timeout reached (when X-Sbr-Timeout-Error: true)

Using with Templates

Use signalTemplateId instead of defining questions inline for standardized research:

{  "contactProfileUrl": "https://linkedin.com/in/johndoe",  "signalTemplateId": "a12b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"}

Templates let you reuse research questions, qualification criteria, and answer types across multiple contacts. Create templates via POST /v1/companies/signals/templates.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.

In: header

Header Parameters

X-Sbr-Timeout-Sec?integer

Custom timeout in seconds (max 900). Defaults to your API key's configured timeout.

Range1 <= value <= 900
X-Sbr-Timeout-Error?string

If "true", returns 408 Request Timeout on timeout instead of 202 with latest status

Value in"true" | "false"

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

contactProfileUrl*string

The contact profile URL to research (e.g., "https://linkedin.com/in/johndoe")

Formaturi
Length1 <= length <= 500
question*string

The research question to ask about the contact

Length1 <= length <= 500
answerType?string

The expected format of the answer

Default"open_text"
Value in"open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"
outputSchema?

JSON Schema defining the expected output structure. Required when answerType is "json_schema". See CreateSignalRequest.outputSchema for full documentation.

webhookUrl?string

Optional webhook URL to receive notifications when processing completes. Webhooks are only sent for freshly processed signals — cached results do not trigger webhooks. Set forceRefresh: true to bypass the cache and ensure webhook delivery.

Formaturi
Lengthlength <= 2048
weight?string

The importance/weight of the signal

Value in"important" | "nice_to_have" | "not_important"
qualificationCriteria?

Qualification criteria mapping answer values based on answerType (same structure as company signals)

signalTemplateId?string

Optional signal template ID to use

Formatuuid
connectors?

Connector configuration for signal generation

verificationMode?string

Controls how strictly Saber verifies answers before responding.

  • strict: Only returns answers backed by verified sources (primary or trusted secondary sources). Returns null when information is unavailable.
  • lenient: Allows logical inference and best-effort estimates when direct evidence is missing. Uses industry benchmarks, logical correlates, and quantitative metrics.
Default"strict"
Value in"strict" | "lenient"
forceRefresh?boolean

Force re-run of the signal analysis, skipping the cache. When set to true, a new signal will be created even if a cached result exists.

Defaultfalse

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/contacts/signals/sync" \  -H "X-Sbr-Timeout-Sec: 120" \  -H "X-Sbr-Timeout-Error: false" \  -H "Content-Type: application/json" \  -d '{    "contactProfileUrl": "https://linkedin.com/in/johndoe",    "question": "What are this person\'s main professional interests?",    "answerType": "open_text"  }'
{  "id": "e45c1dc4-d422-4b51-956b-cb6d3ddaf250",  "status": "completed",  "contactProfileUrl": "https://linkedin.com/in/johndoe",  "question": "What are this person's main professional interests?",  "createdAt": "2024-01-15T10:30:00Z",  "completedAt": "2024-01-15T10:32:15Z",  "answer": {    "type": "open_text",    "openText": {      "value": "Software engineering, AI/ML, and developer tools"    }  },  "confidence": 0.9}
{  "id": "e45c1dc4-d422-4b51-956b-cb6d3ddaf250",  "status": "processing",  "contactProfileUrl": "https://linkedin.com/in/johndoe",  "question": "What are this person's main professional interests?",  "createdAt": "2024-01-15T10:30:00Z"}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}

Create a contact signal asynchronously

POST
/v1/contacts/signals

Submit a LinkedIn URL and question to create a new research signal for an individual contact. The AI will analyze the contact's profile and provide a structured answer based on the specified answer type.

Contact signals work similarly to company signals but focus on individual contacts using their LinkedIn profile URLs.


When to Use This Endpoint

Use this endpoint for event-driven architectures where you need webhook notifications. For simpler integrations, use the /v1/contacts/signals/sync endpoint (recommended) which returns results immediately.


Webhooks

Configure webhooks by including a webhookUrl parameter in your request.

Webhook events:

  • signal.completed - Signal processing finished successfully
  • signal.failed - Signal processing failed

Webhook security: All webhooks include HMAC-SHA256 signatures in the X-Webhook-Signature header for verification.

Webhook reliability:

  • Failed deliveries are retried up to 25 times with exponential backoff
  • Webhook requests timeout after 30 seconds
  • Your endpoint should respond with 2xx status code

Using with Templates

Use signalTemplateId instead of defining questions inline for standardized research:

{  "contactProfileUrl": "https://linkedin.com/in/johndoe",  "signalTemplateId": "a12b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",  "webhookUrl": "https://myapp.com/webhooks/signals"}

Templates let you reuse research questions, qualification criteria, and answer types across multiple contacts. Create templates via POST /v1/companies/signals/templates.


Contact signals work similarly to company signals but focus on individual contacts using their LinkedIn profile URLs.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

contactProfileUrl*string

The contact profile URL to research (e.g., "https://linkedin.com/in/johndoe")

Formaturi
Length1 <= length <= 500
question*string

The research question to ask about the contact

Length1 <= length <= 500
answerType?string

The expected format of the answer

Default"open_text"
Value in"open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"
outputSchema?

JSON Schema defining the expected output structure. Required when answerType is "json_schema". See CreateSignalRequest.outputSchema for full documentation.

webhookUrl?string

Optional webhook URL to receive notifications when processing completes. Webhooks are only sent for freshly processed signals — cached results do not trigger webhooks. Set forceRefresh: true to bypass the cache and ensure webhook delivery.

Formaturi
Lengthlength <= 2048
weight?string

The importance/weight of the signal

Value in"important" | "nice_to_have" | "not_important"
qualificationCriteria?

Qualification criteria mapping answer values based on answerType (same structure as company signals)

signalTemplateId?string

Optional signal template ID to use

Formatuuid
connectors?

Connector configuration for signal generation

verificationMode?string

Controls how strictly Saber verifies answers before responding.

  • strict: Only returns answers backed by verified sources (primary or trusted secondary sources). Returns null when information is unavailable.
  • lenient: Allows logical inference and best-effort estimates when direct evidence is missing. Uses industry benchmarks, logical correlates, and quantitative metrics.
Default"strict"
Value in"strict" | "lenient"
forceRefresh?boolean

Force re-run of the signal analysis, skipping the cache. When set to true, a new signal will be created even if a cached result exists.

Defaultfalse

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/contacts/signals" \  -H "Content-Type: application/json" \  -d '{    "contactProfileUrl": "https://linkedin.com/in/johndoe",    "question": "What are this person\'s main professional interests?",    "answerType": "open_text",    "webhookUrl": "https://myapp.com/webhooks/signals"  }'
{  "id": "e45c1dc4-d422-4b51-956b-cb6d3ddaf250",  "status": "processing",  "contactProfileUrl": "https://linkedin.com/in/johndoe",  "question": "What are this person's main professional interests?",  "createdAt": "2024-01-15T10:30:00Z"}
{  "error": "BAD_REQUEST",  "message": "Invalid JSON in request body"}
{  "error": "UNAUTHORIZED",  "message": "Missing or invalid API key"}
{  "statusCode": 429,  "message": "Rate limit exceeded",  "error": "Too Many Requests",  "retryAfter": 45}
{  "error": {    "type": "VALIDATION",    "code": "VALIDATION_ERROR",    "message": "validation failed for 'departments': invalid value: Engineering Ops",    "errorCode": "string",    "errorAction": "string",    "details": {},    "requestId": "e69ed773-2674-4dca-be61-886d6698f360",    "fields": [      {        "field": "domain",        "message": "'domain' must be a valid hostname (e.g., 'example.com') without protocol (https://) or path"      }    ],    "debug": {      "cause": "string"    }  }}

On this page