API getting started
This page is for someone writing code against 4Bridge. If you are looking for the screens, start at Getting started with 4Bridge.
4Bridge is part of the 4Comply API. Same host, same credentials, same conventions. Every
4Bridge path sits under one prefix, /v1/4bridge/, which makes the product boundary unusually
clean: if the path does not start with that, it is not 4Bridge.
Production is https://api.4comply.io. Every path below is relative to that.
Authentication
Two headers on every call.
Authorization: Bearer sk_eyJhbGciOi...
tenant_id: 6a1f00c2b48e3d5417ab9d20
Both values come from the Integrations tab of Settings in the dashboard, described in
Settings. Tenant ID goes in the tenant_id
header and Secret Key goes in the Authorization header, prefixed with Bearer . The key
already starts with sk_, so do not add that yourself. This is the same pair 4Comply and
4Preferences use, and the same failure modes apply: see
4Preferences API getting started.
The tenant is never read from the body, only from the header, so there is no tenant field to send.
Field naming is not consistent
Bodies and responses are JSON, and 4Bridge does not use one naming convention across all nine route families. Read the reference for the family you are calling rather than assuming.
| Family | Field names |
|---|---|
/connections, /connector-types, /integrations, /tasks |
camelCase, such as connectorType and sourceConnectionId |
/execution-logs |
snake_case, such as task_id, chunks_failed and completed_at |
the response from POST /tasks/{id}/run |
snake_case, execution_id and status, even though the rest of /tasks is camelCase |
| a field mapping inside a task | both, on purpose: sourceField and targetField are the current names, and source_field and dest_field are accepted and returned as aliases of the same values |
TODO(review): confirm whether the snake_case families are frozen for compatibility or due to be renamed. This is the kind of thing that is cheap to fix before a product is documented and expensive afterwards, and 4Bridge has never been documented.
4Bridge endpoints additionally check a role. Reads generally need only a valid credential;
writes are gated on the admin or config role; a small set of operational endpoints is
gated on superadmin.
TODO(review): confirm which role claims a secret key carries. The role check reads the role claims on the token, and a secret key is exchanged for a machine token before it reaches the check, so whether a secret key can create a connection depends on how that machine identity is configured. This is the first question an integrator will hit and this page should answer it outright.
The route families
Nine families, all under /v1/4bridge/.
| Route | Holds | Guide |
|---|---|---|
/connector-types |
the kinds of system 4Bridge can talk to, and the fields each needs | Connections |
/connections |
your configured links to those systems | Connections |
/connector-metadata |
the cached objects and fields read back from a connection | Connector metadata |
/integrations |
pairings of two connections | Integrations |
/tasks |
the jobs that run | Tasks and execution |
/execution-logs |
one record per run | Execution logs |
/webhooks |
inbound trigger endpoints | Webhook triggers |
/4segments |
the 4Segments destination catalogue | The 4Segments bridge |
/admin |
internal operations, not a customer surface | see below |
Connections, integrations and tasks all follow the same shape: GET / lists, GET /{id}
reads one, POST / creates, PUT /{id} updates, DELETE /{id} deletes. Integrations and
tasks add POST /{id}/enable and POST /{id}/disable.
Request and response shapes for every endpoint named on this page are in the 4Bridge API reference beside these guides. Search it for the path.
The smallest end-to-end example
Four writes and one read. Ids from each step feed the next, so run them in order.
1. A connection for each end
curl -s -X POST https://api.4comply.io/v1/4bridge/connections \
-H "Authorization: Bearer sk_..." \
-H "tenant_id: <your tenant id>" \
-H "Content-Type: application/json" \
-d '{
"name": "Marketo Production",
"connectorType": "marketo",
"authType": "oauth2",
"description": "Contact source",
"config": { "<config field name>": "<value>" },
"secrets": { "<secret field name>": "<value>" }
}'
config holds the non-sensitive settings and secrets holds the rest. The split matters:
secrets values are encrypted before storage, config values are not. Both are free-form
maps, and the keys inside them are defined by the connector type, not by this endpoint.
Do not guess them.
Three calls are worth making around this one.
GET /v1/4bridge/connector-typeslists the types and theconnectorTypestring to send.GET /v1/4bridge/connector-types/by-categorygroups them andGET /v1/4bridge/connector-types/categorieslists the categories.GET /v1/4bridge/connector-types/{connectorType}returns the field definitions for that type, which is where theconfigandsecretskey names come from. Each definition gives the field's name, its label, its type, whether it is required, its placeholder, its help text and its validation pattern. Build the body from that response.POST /v1/4bridge/connector-types/{connectorType}/validatechecks aconfigandsecretspair against those definitions and returns the list of validation errors, without contacting the other system.POST /v1/4bridge/connections/testtests credentials before you create anything. It takes aconnectorType,configandsecretsand answers with a success flag and a message.POST /v1/4bridge/connections/{id}/testdoes the same for a saved connection.
For the other end of the flow, use the system-managed connection that already exists. List
connections and look for the one whose connectorType is 4segments, 4comply or
4preferences; those are marked isSystemManaged and you cannot create or change them.
Once a connection exists, GET /v1/4bridge/connections/{id}/objects lists what it can read or
write and GET /v1/4bridge/connections/{id}/objects/{objectName}/fields lists an object's
fields. Those two are how you discover the names to use in the next steps.
2. An integration pairing them
curl -s -X POST https://api.4comply.io/v1/4bridge/integrations \
-H "Authorization: Bearer sk_..." \
-H "tenant_id: <your tenant id>" \
-H "Content-Type: application/json" \
-d '{
"name": "Marketo and 4Segments",
"description": "Contact load",
"sourceConnectionId": "<the marketo connection id>",
"targetConnectionId": "<the 4segments connection id>",
"isPublished": true
}'
isPublished is the enabled flag. POST /v1/4bridge/integrations/{id}/disable turns it off
later without deleting anything, which is also what you have to do before a delete is allowed.
3. A task inside it
curl -s -X POST https://api.4comply.io/v1/4bridge/tasks \
-H "Authorization: Bearer sk_..." \
-H "tenant_id: <your tenant id>" \
-H "Content-Type: application/json" \
-d '{
"integrationId": "<the integration id>",
"name": "Load Marketo leads",
"sourceObject": "Leads",
"targetObject": "contacts",
"action": "Upsert",
"matchField": "email_address",
"scheduleType": "24h",
"isEnabled": true,
"chunkSize": 5000,
"retryAttempts": 3,
"fieldMappings": [
{ "sourceField": "email", "targetField": "email_address" },
{ "sourceField": "firstName", "targetField": "first_name" }
]
}'
actionis the sync action, described on Sync rules. The dashboard labels them Create, Upsert, Update, Delete and Sync, and the review step displays the saved value in lower case. TODO(review): confirm whether the field is compared case-insensitively, because the API stores whatever string you send.matchFieldnames the destination field used to find an existing record. It is required for anything exceptCreateand should be unique.scheduleTypeismanual,webhook,cron, or one of the six intervals5min,30min,1h,6h,12hand24h. Withcron, also sendcronExpression, and note that it is read as UTC.fieldMappingspairssourceFieldwithtargetField, and also carries the legacy aliases described above, so a task read back and written straight out again round-trips safely.sourceConfigis a free-form string map for connector-specific read settings, such as the MySQL read mode and its timestamp column, or a Marketo static list and its error list.fourSegmentsDesignis the extra payload sent when the destination is 4Segments, and it is what triggersCREATE TABLEorALTER TABLEon save. Itsstrategyiscreate_neworreuse. See The 4Segments bridge.
The create is validated before it is stored, and a validation failure comes back as 400
with a list of messages rather than one string. For a MySQL source those messages are
specific, naming the missing timestamp column or the disallowed filter operator.
4. Run it and read the log
curl -s -X POST https://api.4comply.io/v1/4bridge/tasks/<the task id>/run \
-H "Authorization: Bearer sk_..." \
-H "tenant_id: <your tenant id>"
The response is the execution it queued, not the result:
{ "execution_id": "0f5c9a2e-1d43-4b7a-9c8e-2a6b71d0f4c1", "status": "Queued" }
Then poll the log:
curl -s "https://api.4comply.io/v1/4bridge/execution-logs/<the execution id>" \
-H "Authorization: Bearer sk_..." \
-H "tenant_id: <your tenant id>"
Reading one log by id returns more than the list does: the per-step trace, the failing records, and, when the task has debug mode on, samples of the source and destination records.
GET /v1/4bridge/execution-logs lists them, filtered by taskId, status, from, to and
limit (100 by default). GET /v1/4bridge/execution-logs/summary aggregates per task, giving
a run count, a success count, a failure count, a partial count, a success rate and the last
run time for the ten busiest tasks.
The statuses to branch on are Queued, Running, Success, PartialSuccess and Failed.
Their meanings, and why PartialSuccess needs its own branch, are on
Execution logs.
The ids in the examples above are illustrative, in the shape the API returns.
TODO(review): confirm the intended polling interval and whether there is any push alternative
for run completion. PartialSuccess and Failed are exactly the cases an integrator wants to
be told about, and the only mechanism today is polling.
Getting told when something changes, and when you cannot
4Bridge webhooks are inbound only: they give a task a URL that an external system POSTs to in order to trigger a run. There is no outbound webhook that tells you a run finished.
curl -s -X POST https://api.4comply.io/v1/4bridge/webhooks/endpoints \
-H "Authorization: Bearer sk_..." \
-H "tenant_id: <your tenant id>" \
-H "Content-Type: application/json" \
-d '{ "taskId": "<the task id>", "sourceSystem": "marketo" }'
The endpoint that gets called is anonymous, one per task, and signed with an
X-Webhook-Signature header carrying a base64 HMAC-SHA256 of the raw body. The full contract,
including the response codes and the record_ids body, is on
Webhook triggers.
If you need to know about events in the rest of the platform, those products have outbound webhooks of their own: 4Comply webhooks and 4Preferences webhooks.
Endpoints that are not for you
Four groups appear in the reference and are not customer integration points. They are listed here so nobody wires a secret key into one by mistake.
| Endpoint | Why not |
|---|---|
everything under /v1/4bridge/admin |
worker pause and resume, deployment readiness and the tenant audit log. The first three require the superadmin role, which is 4thought Marketing operations, not a customer role. |
GET /v1/4bridge/connections/{id}/runtime-credentials |
returns the decrypted credentials of a connection. It exists so the worker can authenticate. Its own comment says never to call it from a user-facing interface. |
GET /v1/4bridge/4segments/storage-credentials |
returns the real MySQL credentials of your 4Segments schema, for 4Segments to register it as a datasource. Also returns secrets. |
the /v1/4bridge/tasks/_debug/... paths |
temporary diagnostics. They take no authentication and no tenant filter, and the code comments say they are to be removed. |
GET /v1/4bridge/admin/audit-logs is the one in that group with obvious customer value: it
returns every configuration change in the tenant, with who made it, what changed, the before
and after values and the caller's IP address. It is gated on admin as well as superadmin,
so an administrator can read it.
TODO(review): confirm whether the tenant audit log should be promoted out of /admin and
documented as a customer feature, and whether the four _debug and credential endpoints
should be marked [InternalApi] so they leave the published reference entirely.
TODO(review): POST /v1/4bridge/connector-types/admin/seed and the three other
/connector-types/admin writes let an admin role create and delete connector type
definitions, which are platform-wide rather than per tenant. Confirm whether a customer
administrator should be able to reach them at all.