Universal API & Webhook Ingestion
Overview
CRED supports two mechanisms for ingesting external data into the import system:
| Feature | Webhook Ingestion | Universal API |
|---|---|---|
| Direction | Push - external systems send data to CRED | Pull - CRED fetches data from external APIs |
| Source Type | WEBHOOK |
UNIVERSAL_API |
| Trigger | External HTTP POST to a webhook URL | Manual (GraphQL), scheduled, or workflow |
| Authentication | Secret-based (X-Cred-Webhook-Secret header) |
Basic Auth, custom headers per configuration |
| Entity Types | CONTACT, ACCOUNT, CUSTOMER, COMMERCIAL_PRODUCT, OPPORTUNITY, DOCUMENT | CONTACT, ACCOUNT, CUSTOMER, COMMERCIAL_PRODUCT, OPPORTUNITY |
Both flows share the same downstream import processing pipeline: Import -> ImportRecord -> ImportField -> Entity creation.
Architecture
Database Tables
Webhook tables
| Table | Purpose |
|---|---|
UniversalWebhook |
Webhook definitions (name, URL, secret, entity type, active status) |
Universal API tables
| Table | Purpose |
|---|---|
UniversalApiAccount |
Groups API configurations under a company (status: ACTIVE/INACTIVE) |
UniversalApiConfiguration |
Individual endpoint configuration (URL, method, headers, auth, retry, schedule) |
UniversalApiExecutionLog |
Audit log for each API execution (request/response details, timing, errors) |
Shared import tables
| Table | Purpose |
|---|---|
Import |
Parent record with sourceType = WEBHOOK or UNIVERSAL_API |
ImportRecord |
Individual data rows received |
ImportField |
Column/field definitions extracted from data |
Data Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WEBHOOK FLOW β
β β
β External System β
β β β
β βΌ β
β POST /universal-webhook/:ulid β
β (X-Cred-Webhook-Secret header) β
β β β
β βΌ β
β processWebhookData use case β
β β β
β βββ Creates/finds Import (sourceType: WEBHOOK) β
β βββ Creates ImportRecord rows β
β βββ Auto-transitions status to UPLOADING β
β β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββ
β SHARED PIPELINE β
β β
β SETUP_IMPORTS task β
β (extracts fields) β
β β β
β βΌ β
β Map fields (GraphQL) β
β β β
β βΌ β
β START_IMPORT_FILE_DATAβ
β (creates entities) β
ββββββββββββββββββββββββββ
β²
β
ββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββ
β UNIVERSAL API FLOW β
β β
β executeUniversalApiRequest (GraphQL mutation) β
β β β
β βΌ β
β ApiRequestExecutor β
β (HTTP/GraphQL request to external API) β
β β β
β βββ Creates/finds Import (sourceType: UNIVERSAL_API)β
β βββ Creates ImportRecord rows from response β
β βββ Logs execution to UniversalApiExecutionLog β
β βββ Status set to UPLOADING β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Webhook Ingestion
How It Works
- Create a webhook via GraphQL with a target entity type (for example,
CONTACT). - The system generates a unique URL (
/universal-webhook/<ULID>) and a secret. - Configure the external system to POST JSON to this URL with
X-Cred-Webhook-Secret. - CRED creates (or reuses) an
Import, appendsImportRecordrows, and sets status toUPLOADING.
Webhook URL Format
https://<host>/universal-webhook/<ULID>
Accepted Payload Formats
Array at root
[
{ "firstName": "John", "email": "john@example.com" },
{ "firstName": "Jane", "email": "jane@example.com" }
]
Array in data field
{
"data": [
{ "firstName": "John", "email": "john@example.com" }
]
}
Document Webhooks
For DOCUMENT entity type webhooks, two additional endpoints are available:
POST /universal-webhook/:ulid/documentfor multipart file upload (PDF only)POST /universal-webhook/:ulid/document-jsonfor JSON payload with base64 PDF or public URL
GraphQL Operations
Create webhook
mutation {
createWebhook(input: {
name: "My Webhook"
entityType: CONTACT
description: "Receives contacts from external CRM"
}) {
id
url
secret
}
}
Update webhook
mutation {
updateWebhook(input: {
id: 1
name: "Updated Name"
}) {
id
name
}
}
Delete webhook
mutation {
deleteWebhook(id: 1)
}
Universal API
How It Works
- Create a Universal API Account as a company-level grouping.
- Create one or more Configurations under that account (per endpoint).
- Use test connection to validate connectivity without saving data.
- On execution (manual, schedule, workflow), CRED fetches data, creates
Import+ImportRecord, and logs execution.
Supported Request Types
| Type | Description |
|---|---|
HTTP |
Standard REST API calls (GET/POST) |
GRAPHQL |
GraphQL requests (body with query and variables) |
Configuration Options
| Field | Required | Description |
|---|---|---|
universalApiAccountId |
Yes | Parent account ID |
requestType |
Yes | HTTP or GRAPHQL |
entityType |
Yes | Target entity type (CONTACT, ACCOUNT, etc.) |
url |
Yes | API endpoint URL |
method |
No | GET or POST (default: GET for HTTP) |
headers |
No | Custom headers as JSON string |
queryString |
No | Query parameters as JSON string |
body |
No | Request body as JSON string |
bodyType |
No | JSON, XML, form-data, x-www-form-urlencoded, GRAPHQL |
basicAuthUsername |
No | Basic Auth username |
basicAuthPassword |
No | Basic Auth password (encrypted at rest) |
timeoutSeconds |
No | Request timeout (1-300 seconds) |
retryCount |
No | Number of retries on failure (0-10) |
retryDelayMillis |
No | Delay between retries in ms (0-60000) |
scheduleEnabled |
No | Enable scheduled execution (runs 3x daily) |
Execution Triggers
| Trigger | Description |
|---|---|
MANUAL |
Triggered via executeUniversalApiRequest GraphQL mutation |
SCHEDULE |
Automatic execution when scheduleEnabled is true |
WORKFLOW |
Triggered by workflow action EXECUTE_UNIVERSAL_API_REQUEST |
Execution Logging
Each execution (success or failure) is stored in UniversalApiExecutionLog with:
- Request details (URL, method, headers, body, query params)
- Response details (status code, headers)
- Timing (
startedAt,endedAt,executionTime) - Created record count
- Error details (if any, truncated to 500 chars)
- Trigger metadata (type and source)
Logs older than 90 days are cleaned up automatically.
GraphQL Operations
Create account
mutation {
createUniversalApiAccount(input: {
status: ACTIVE
description: "My API integrations"
}) {
id
}
}
Create configuration
mutation {
createUniversalApiConfiguration(input: {
universalApiAccountId: 1
requestType: HTTP
entityType: CONTACT
url: "https://api.example.com/contacts"
method: GET
headers: "{\"Authorization\": \"Bearer token123\"}"
timeoutSeconds: 30
retryCount: 3
retryDelayMillis: 1000
}) {
id
}
}
Test connection (no data imported)
mutation {
testUniversalApiConnection(input: {
requestType: HTTP
url: "https://api.example.com/contacts"
method: GET
headers: "{\"Authorization\": \"Bearer token123\"}"
}) {
success
statusCode
executionTime
parsedData
}
}
Execute request (imports data)
mutation {
executeUniversalApiRequest(universalApiConfigurationId: 1) {
success
statusCode
recordsCreated
executionTime
}
}
Query accounts and configurations
query {
universalApiAccounts(first: 10) {
edges {
node {
id
status
description
configurations {
id
url
method
entityType
scheduleEnabled
}
}
}
}
}
Delete configuration/account
mutation { deleteUniversalApiConfiguration(id: 1) }
mutation { deleteUniversalApiAccount(id: 1) }
Import Processing Pipeline
After ingestion from webhook or Universal API, data flows through the standard import pipeline.
Step 1: SETUP_IMPORTS (Worker Task)
- Extracts keys from the first
ImportRecord - Creates one
ImportFieldper unique key
Step 2: Field Mapping
Users map extracted fields to CRED data descriptions:
mutation {
setImportFields(importId: 1, fields: [
{ id: 10, dataDescriptionId: 5 }
]) {
id
}
}
Step 3: START_IMPORT_FILE_DATA (Worker Task)
- Reads mapped fields
- Creates/updates target entities (Contact, Account, and others)
- Tags entities by source type (
IMPORT_WEBHOOKorIMPORT_UNIVERSAL_API) - Updates
ImportRecord.entityIdandImportRecord.isImported
Security
Webhook Security
- Each webhook has a unique cryptographic secret
- Requests must include
X-Cred-Webhook-Secret - Secret check uses constant-time comparison (
crypto.timingSafeEqual) - Inactive webhooks reject requests with HTTP 403
Universal API Security
- Basic Auth credentials are encrypted at rest
- Configurations are tenant-scoped to company
- SSRF protections block private/internal/loopback targets
- Company-level authorization is enforced across all operations
Enums Reference
ImportSourceTypeEnum
| Value | Usage |
|---|---|
FILE |
CSV/file uploads |
EMAIL |
Email imports |
WEBHOOK |
Webhook ingestion |
UNIVERSAL_API |
Universal API pull |
MERGE_* |
CRM sync integrations |
UniversalApiExecutionStatus
| Value | Description |
|---|---|
PENDING |
Execution queued |
RUNNING |
Execution in progress |
SUCCESS |
Completed successfully |
FAILED |
Completed with errors |
UniversalApiAccountStatus
| Value | Description |
|---|---|
ACTIVE |
Account can execute configurations |
INACTIVE |
Account is disabled and executions are skipped |