Creating a Signal
Create a company signal synchronously
⭐ 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
- Send your signal creation request (same payload as regular POST /v1/companies/signals)
- The endpoint creates the signal and polls for completion
- Returns the completed signal with results when processing finishes
- 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-Secheader to set custom timeout (up to max) - Error on timeout: Use
X-Sbr-Timeout-Error: trueheader to return 408 on timeout instead of latest status
Response Codes
200 OK- Signal completed successfully202 Accepted- Timeout reached, signal still processing408 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 API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.
In: header
Header Parameters
Custom timeout in seconds (max 900). Defaults to your API key's configured timeout.
1 <= value <= 900If "true", returns 408 Request Timeout on timeout instead of 202 with latest status
"true" | "false"Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
The company domain to research (e.g., "acme.com")
^[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])?)*$1 <= length <= 253The research question to ask about the company
1 <= length <= 500The expected format of the answer
"open_text""open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"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, nullproperties: Define object propertiesrequired: List of required property namesitems: Schema for array itemsenum: Enumeration of allowed valuesdescription: Property descriptions (helps AI generate better data)
Limitations:
- Maximum 100 total properties
- Maximum 5 levels of nesting
anyOf,oneOf,allOfare not supported$refonly supports self-references for recursive schemas
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.
urilength <= 2048The importance/weight of the signal
"important" | "nice_to_have" | "not_important"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
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.
uuidForce 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.
falseConnector configuration for signal generation
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.
"strict""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
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 successfullysignal.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 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.
The company domain to research (e.g., "acme.com")
^[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])?)*$1 <= length <= 253The research question to ask about the company
1 <= length <= 500The expected format of the answer
"open_text""open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"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, nullproperties: Define object propertiesrequired: List of required property namesitems: Schema for array itemsenum: Enumeration of allowed valuesdescription: Property descriptions (helps AI generate better data)
Limitations:
- Maximum 100 total properties
- Maximum 5 levels of nesting
anyOf,oneOf,allOfare not supported$refonly supports self-references for recursive schemas
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.
urilength <= 2048The importance/weight of the signal
"important" | "nice_to_have" | "not_important"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
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.
uuidForce 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.
falseConnector configuration for signal generation
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.
"strict""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
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:
- Inline Questions: Define questions directly in the request for ad-hoc research without creating templates
- 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
batchIdfor 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/syncendpoint (recommended) to retrieve results as they complete
Webhooks:
- Each signal in the batch can include a
webhookUrlto receive notifications on completion - Webhooks fire individually per signal as each one completes processing
- Webhook events:
signal.completed(success) orsignal.failed(failure) - All webhooks include HMAC-SHA256 signatures in the
X-Webhook-Signatureheader
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: trueon individual signals
Automatic Summary Generation:
- Set
generateSummaryOnCompletetotrueto 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/summariesendpoint to retrieve generated summaries
Authorization
ApiKeyAuth 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.
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
questionand optional fields (quick ad-hoc research) - Use
templateIdto reference a pre-created template (recommended for reusability)
1 <= itemsArray 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.
1 <= items <= 100Whether 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.
falseWhen 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.
falseResponse 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
⭐ 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
- Send your signal creation request (same payload as regular POST /v1/contacts/signals)
- The endpoint creates the signal and polls for completion
- Returns the completed signal with results when processing finishes
- 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-Secheader to set custom timeout (up to max) - Error on timeout: Use
X-Sbr-Timeout-Error: trueheader to return 408 on timeout instead of latest status
Response Codes
200 OK- Signal completed successfully202 Accepted- Timeout reached, signal still processing408 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 API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.
In: header
Header Parameters
Custom timeout in seconds (max 900). Defaults to your API key's configured timeout.
1 <= value <= 900If "true", returns 408 Request Timeout on timeout instead of 202 with latest status
"true" | "false"Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
The contact profile URL to research (e.g., "https://linkedin.com/in/johndoe")
uri1 <= length <= 500The research question to ask about the contact
1 <= length <= 500The expected format of the answer
"open_text""open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"JSON Schema defining the expected output structure. Required when answerType is "json_schema". See CreateSignalRequest.outputSchema for full documentation.
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.
urilength <= 2048The importance/weight of the signal
"important" | "nice_to_have" | "not_important"Qualification criteria mapping answer values based on answerType (same structure as company signals)
Optional signal template ID to use
uuidConnector configuration for signal generation
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.
"strict""strict" | "lenient"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.
falseResponse 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
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 successfullysignal.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 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.
The contact profile URL to research (e.g., "https://linkedin.com/in/johndoe")
uri1 <= length <= 500The research question to ask about the contact
1 <= length <= 500The expected format of the answer
"open_text""open_text" | "number" | "boolean" | "list" | "percentage" | "currency" | "url" | "contacts" | "contacts_generation" | "contact_posts" | "contact_engagements" | "json_schema"JSON Schema defining the expected output structure. Required when answerType is "json_schema". See CreateSignalRequest.outputSchema for full documentation.
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.
urilength <= 2048The importance/weight of the signal
"important" | "nice_to_have" | "not_important"Qualification criteria mapping answer values based on answerType (same structure as company signals)
Optional signal template ID to use
uuidConnector configuration for signal generation
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.
"strict""strict" | "lenient"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.
falseResponse 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" } }}