API getting started

Source: content/4bridge/developers/api-getting-started.md

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-types lists the types and the connectorType string to send. GET /v1/4bridge/connector-types/by-category groups them and GET /v1/4bridge/connector-types/categories lists the categories.
  • GET /v1/4bridge/connector-types/{connectorType} returns the field definitions for that type, which is where the config and secrets key 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}/validate checks a config and secrets pair against those definitions and returns the list of validation errors, without contacting the other system.
  • POST /v1/4bridge/connections/test tests credentials before you create anything. It takes a connectorType, config and secrets and answers with a success flag and a message. POST /v1/4bridge/connections/{id}/test does 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" }
        ]
      }'
  • action is 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.
  • matchField names the destination field used to find an existing record. It is required for anything except Create and should be unique.
  • scheduleType is manual, webhook, cron, or one of the six intervals 5min, 30min, 1h, 6h, 12h and 24h. With cron, also send cronExpression, and note that it is read as UTC.
  • fieldMappings pairs sourceField with targetField, and also carries the legacy aliases described above, so a task read back and written straight out again round-trips safely.
  • sourceConfig is 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.
  • fourSegmentsDesign is the extra payload sent when the destination is 4Segments, and it is what triggers CREATE TABLE or ALTER TABLE on save. Its strategy is create_new or reuse. 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.