Integrations Guide
How to connect Salesforce, HubSpot, and Stripe — including multiple instances of the same provider — run syncs, view events, and troubleshoot.
HubSpot
Connecting via OAuth
- Log in to the Anchord Console
- Navigate to Integrations
- Click Connect next to HubSpot
- Authorize in HubSpot
- The callback creates the integration and stores tokens
Running a sync
Navigate to Integrations → HubSpot and click Sync now. The sync pulls companies and contacts updated since the last cursor, ingests them via the batch API, and records a SyncRun.
Or via the API:
curl -s -X POST https://api.anchord.ai/app/integrations/hubspot/sync \
-H "Authorization: Bearer <your-key>" What the sync does
1. Resolve cursor (last sync timestamp, default: 24h ago)
2. Refresh access token if expired
3. Pull companies updated since cursor (paginated, 100/page)
4. Pull contacts updated since cursor (paginated, 100/page)
5. Map each to ingest records (system=hubspot)
6. POST /api/v1/ingest/batch in chunks of 100
7. Record SyncRun with fetched/ingested/failed counts
8. Update cursor to max(lastmodifieddate)
Disconnecting
Navigate to Integrations → HubSpot and click Disconnect. This wipes all stored secrets and marks the integration as disconnected.
Salesforce
Connecting via OAuth
- Log in to the Anchord Console
- Navigate to Integrations → Salesforce
- Choose your environment: Production, Sandbox, or Custom domain
- Click Connect Salesforce
- Authorize in Salesforce (read-only scopes:
api,refresh_token,id) - The callback stores tokens and detects Person Accounts automatically
Sync model
Navigate to Integrations → Salesforce and click Sync now. Two jobs are queued — one for Accounts, one for Contacts — and run asynchronously.
Or via the API:
curl -s -X POST https://api.anchord.ai/app/integrations/salesforce/sync \
-H "Authorization: Bearer <your-key>" 1. Resolve per-object cursor (or backfill window: 30/90/365 days / all)
2. Refresh access token if expired (401 retry)
3. SOQL query Accounts/Contacts by SystemModstamp (paginated)
4. Map to ingest records (system=salesforce, object_type=account|contact)
5. POST /api/v1/ingest/batch in chunks of 100
6. Record SyncRun with fetched/ingested/failed counts
7. Nightly sync at 02:30 UTC for all connected integrations
Person Accounts
If your Salesforce org has Person Accounts enabled, Anchord detects
this automatically on connect. Person Account records are ingested as
object_type=person_account with person-level fields
(name, email, phone, mailing address). Business accounts remain
object_type=account.
Push-based ingest (alternative)
You can also push Salesforce data directly via the ingest API — useful for agent-driven workflows or custom ETL pipelines.
curl -s -X POST https://api.anchord.ai/api/v1/ingest/batch \
-H "Authorization: Bearer <your-key>" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"system": "salesforce",
"object_type": "account",
"object_id": "001A000001ABC",
"payload": {"name": "Acme Corp", "domain": "acme.com"}
},
{
"system": "salesforce",
"object_type": "contact",
"object_id": "003B000002DEF",
"payload": {"first_name": "Jane", "last_name": "Smith", "email": "jane@acme.com"}
}
]
}' Disconnecting
Navigate to Integrations → Salesforce and click
Disconnect. Data is preserved by default. Optionally
check "Also purge Salesforce data" and type PURGE SALESFORCE
to remove all SF source records and entity links.
Stripe
Webhook processing
Stripe events are received via POST /webhooks/stripe.
The signature is verified (HMAC-SHA256), the event is persisted
idempotently, and a processing job is dispatched.
| Stripe event prefix | Object type |
|---|---|
| customer.* | customer |
| subscription.* | subscription |
| invoice.* | invoice |
Idempotency
If the same Stripe event is delivered twice, the second request
returns {"status": "already_received"} with HTTP 200.
No duplicate rows are created.
Custom Sources
Beyond HubSpot and Stripe, you can ingest data from any system by registering a custom source.
# Register a custom source
curl -s -X POST https://api.anchord.ai/api/v1/sources \
-H "Authorization: Bearer <your-key>" \
-H "Content-Type: application/json" \
-d '{"key": "my_crm", "display_name": "My CRM"}'
# Ingest with the custom source
curl -s -X POST https://api.anchord.ai/api/v1/ingest/batch \
-H "Authorization: Bearer <your-key>" \
-H "Content-Type: application/json" \
-d '{
"records": [{
"system": "my_crm",
"object_type": "company",
"object_id": "c-123",
"payload": {"name": "Globex", "domain": "globex.com"}
}]
}'
You can also manage sources from the Sources page in the console.
Built-in keys (hubspot, stripe) are reserved.
Viewing sync runs
Navigate to Sync Runs in the console to see a paginated table of all sync runs across providers: run ID, provider, type (manual/nightly), status, fetched/ingested/failed counts, started time, and duration. Click into any run for full details.
Troubleshooting
Every API response includes a request_id. Use it to
correlate logs across sync runs, webhook events, and API errors.
| Symptom | Likely cause | Fix |
|---|---|---|
| Sync status = error, 401 | Token expired | Disconnect and reconnect |
| Webhook events stuck | Queue worker not running | Start queue worker |
| Webhook returns 401 | Signing secret mismatch | Re-register webhook |
| 0 records fetched | Cursor ahead of data | Reset sync cursor |
| Ingest validation errors | Missing required fields | Check system, object_type, object_id, payload |