Salesforge API GTM connection loop
This image shows the Salesforge API GTM connection loop

If your GTM team is still exporting HubSpot lists, cleaning fields in spreadsheets, importing contacts into outreach tools, and asking reps to update CRM after replies, you do not have a connected outbound system.

You have manual middleware.

The Salesforge API is how I would connect Salesforge to the rest of a GTM stack: CRM, enrichment, data warehouse jobs, workflow tools, AI agents, and reporting. The implementation starts with a simple pattern: authenticate, find the workspace, create or update contacts, assign them to a sequence, validate before launch, then send reply events back through webhooks.

Here is the direct shape of the REST API:

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/contacts" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "firstName": "Sarah",
    "lastName": "Jones",
    "email": "[email protected]",
    "company": "Acme",
    "position": "VP Sales",
    "linkedinUrl": "https://www.linkedin.com/in/sarah-jones",
    "tags": ["hubspot", "outbound-qualified"],
    "customVars": {
      "pain_point": "manual CRM cleanup",
      "source_system": "hubspot"
    }
  }' 

Use the API when your workflow needs custom routing, governance, retries, and feedback loops. Use the native Salesforge integrations when standard sync is enough.

TL;DR: The Salesforge API Connection Pattern

The base URL for the Salesforge v2 API is:

https://api.salesforge.ai/public/v2

Authentication uses an API key in the Authorization header:

Authorization: YOUR_SALESFORGE_API_KEYAuthorization: YOUR_SALESFORGE_API_KEY

For a normal GTM workflow, I would build this order:

  1. GET /workspaces to find the workspace ID.
  2. GET /workspaces/{workspaceID}/dnc to sync do-not-contact records before sending.
  3. POST /workspaces/{workspaceID}/contacts or POST /workspaces/{workspaceID}/contacts/bulk to create contacts.
  4. GET /workspaces/{workspaceID}/sequences?statuses[]=paused&type=legacy or the multichannel sequence endpoints to select the right campaign.
  5. PUT /workspaces/{workspaceID}/sequences/{sequenceID}/contacts to assign contacts.
  6. POST /workspaces/{workspaceID}/sequences/{sequenceID}/contacts/validation/start to validate contacts before launch.
  7. POST /workspaces/{workspaceID}/integrations/webhooks to send replies, bounces, unsubscribes, meetings, and label changes back to your stack.
  8. PUT /workspaces/{workspaceID}/sequences/{sequenceID}/status only when your governance rules allow the sequence to move from paused to active.

That is the minimum useful connection. Anything less usually becomes another brittle lead handoff.

Salesforge API Swagger documentation
This image shows the Salesforge API Swagger documentation

Before You Build: Native Integration Or API?

Do not build custom API plumbing just because you can.

Salesforge already supports native or packaged integrations for common GTM tools. If the standard integration handles your sync, use it. Custom code should earn its keep.

Choose Native Integration When Choose the API When
Standard contact sync is enough Routing depends on territory, account tier, or custom rules
Default field mapping works You need warehouse, enrichment, or product data in the decision
Standard events cover the workflow Multiple systems must be orchestrated together
You do not need custom approvals You need approval gates, audit logs, or sequence governance
RevOps does not want to maintain code You need retries, idempotency, observability, and rollback control

The API makes the handoff layer programmable, but it does not replace the ownership, field definitions, and routing rules required to make that layer work.

If CRM ownership is confused, qualification is vague, DNC rules are unclear, or nobody owns sequence logic, the API will only move bad decisions faster.

The Salesforge API Endpoint Map I Would Start With

Salesforge API objects for GTM workflows
This image shows the Salesforge API objects for GTM workflows

Here is the practical map I would give a GTM engineer before implementation.

Task Method Endpoint Required IDs Constraint
Check API key user GET /me API key Confirms the key works
List workspaces GET /workspaces API key Uses limit and offset
Get DNC entries GET /workspaces/{workspaceID}/dnc Workspace Uses limit and offset
List contacts GET /workspaces/{workspaceID}/contacts Workspace Filter by tags, sequence, or validation status
Create contact POST /workspaces/{workspaceID}/contacts Workspace firstName is required
Bulk create contacts POST /workspaces/{workspaceID}/contacts/bulk Workspace 1 to 100 contacts per request
List sequences GET /workspaces/{workspaceID}/sequences Workspace Filter by status, product, ID, or type
Assign contacts PUT /workspaces/{workspaceID}/sequences/{sequenceID}/contacts Workspace, sequence, contacts Returns 204 on success
Assign mailboxes PUT /workspaces/{workspaceID}/sequences/{sequenceID}/mailboxes Workspace, sequence, mailboxes Requires mailbox IDs
Start validation POST /workspaces/{workspaceID}/sequences/{sequenceID}/contacts/validation/start Workspace, sequence Returns 202 when started
Get validation result GET /workspaces/{workspaceID}/sequences/{sequenceID}/contacts/validation/result Workspace, sequence Status is in_progress or completed
Update sequence status PUT /workspaces/{workspaceID}/sequences/{sequenceID}/status Workspace, sequence Body status is active or paused
Create webhook POST /workspaces/{workspaceID}/integrations/webhooks Workspace Type and URL are required
List Primebox labels GET /workspaces/{workspaceID}/primebox-labels Workspace Uses limit and offset
List threads GET /workspaces/{workspaceID}/threads Workspace Filter by mailbox, agent, sequence, label, or sentiment
Update thread label PUT /workspaces/{workspaceID}/mailboxes/{mailboxID}/threads/{threadID}/label Workspace, mailbox, thread, label Returns 204 on success
Reply to email POST /workspaces/{workspaceID}/mailboxes/{mailboxID}/emails/{emailID}/reply Workspace, mailbox, email Returns 204 on success
Reply on LinkedIn POST /workspaces/{workspaceID}/threads/{threadID}/linkedin/reply Workspace, thread, account Requires accountId

For multichannel sequence building, Salesforge also exposes a separate multichannel surface on https://multichannel-api.salesforge.ai/public. That includes actions, conditions, LinkedIn accounts, sender profiles, sequences, nodes, branches, enrollments, launch, settings, schedule, analytics, and validation runs.

The important distinction: do not claim every UI capability is available through a public REST operation unless you can map it to an endpoint. The table above is the practical build surface.

Step 1: Authenticate And Find Your Workspace

Salesforge API keys are generated inside Salesforge under Settings -> API. Store the key in a secret manager, not in Zapier notes, local scripts, or a shared spreadsheet.

Salesforge API key
This image shows the Salesforge API key

I would first verify the key:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/me" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Then list workspaces:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces?limit=10&offset=0" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Example response shape:

{
  "data": [
    {
      "id": "wk_123",
      "name": "North America Outbound",
      "slug": "north-america-outbound",
      "accountId": "acct_123"
    }
  ],
  "limit": 10,
  "offset": 0,
  "total": 1
} 

If your company has multiple regions, clients, products, or agencies inside Salesforge, this workspace decision matters. It controls the boundary for contacts, sequences, mailboxes, labels, DNC entries, and webhooks.

For an agency, I would usually separate workspaces by client. For a GTM team, I would separate only when permissions, sender infrastructure, reporting, or campaign ownership need a real boundary.

Step 2: Check DNC Before Creating Campaign Work

This is where many API projects go wrong.

They create contacts first, then worry about exclusions later. I would do the opposite.

Sync do-not-contact entries before assigning anything to a campaign:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/dnc?limit=100&offset=0" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Use your own CRM, legal, and customer lists too. Salesforge can store DNC records, but your integration should still check internal exclusion rules before creating outreach work.

The API does not decide who you are allowed to contact. Your GTM governance does.

Step 3: Create A Contact From CRM Or Enrichment Data

A single contact request is the easiest first test.

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/contacts" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "firstName": "Sarah",
    "lastName": "Jones",
    "email": "[email protected]",
    "company": "Acme",
    "position": "VP Sales",
    "linkedinUrl": "https://www.linkedin.com/in/sarah-jones",
    "tags": ["hubspot", "tier-1", "q3-outbound"],
    "tagIds": ["tag_123"],
    "customVars": {
      "crm_contact_id": "hubspot-98765",
      "territory": "NA",
      "segment": "B2B SaaS",
      "pain_point": "manual lead routing",
      "source_system": "hubspot"
    }
  }' 

The request body supports firstName, lastName, email, company, position, linkedinUrl, tags, tagIds, and customVars. The spec requires firstName; in real GTM work, I would also require email or LinkedIn URL at the workflow layer so the contact can actually be used.

Example response shape:

{
  "id": "contact_123",
  "firstName": "Sarah",
  "lastName": "Jones",
  "email": "[email protected]",
  "company": "Acme",
  "linkedinUrl": "https://www.linkedin.com/in/sarah-jones",
  "tags": ["hubspot", "tier-1", "q3-outbound"],
  "customVars": {
    "crm_contact_id": "hubspot-98765",
    "territory": "NA",
    "segment": "B2B SaaS"
  }
} 

The customVars object is where your GTM stack becomes useful. If HubSpot, Salesforce, Clay, Leadsforge, or your warehouse has a buying signal, pain point, competitor mention, hiring trigger, or account tier, pass it into Salesforge as a field your sequence can use.

Otherwise personalization stays trapped upstream.

Step 4: Bulk Create Contacts Without Losing Control

For campaign loading, use bulk creation.

The endpoint supports 1 to 100 contacts per request:

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/contacts/bulk" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "contacts": [
      {
        "firstName": "Sarah",
        "lastName": "Jones",
        "email": "[email protected]",
        "company": "Acme",
        "position": "VP Sales",
        "tags": ["hubspot", "tier-1"],
        "customVars": {
          "crm_contact_id": "hubspot-98765",
          "source_system": "hubspot"
        }
      },
      {
        "firstName": "Dev",
        "lastName": "Patel",
        "email": "[email protected]",
        "company": "Northstar",
        "position": "Head of Growth",
        "tags": ["clay", "intent-signal"],
        "customVars": {
          "intent_signal": "new sales hiring push",
          "source_system": "clay"
        }
      }
    ]
  }' 

Do not use bulk creation as an excuse to dump every record into Salesforge.

I would batch only contacts that passed:

  • DNC and customer suppression checks.
  • Duplicate checks in CRM and Salesforge.
  • Required field checks for first name, company, channel, and personalization variables.
  • Email quality checks from your enrichment layer.
  • Territory, owner, and account-stage rules.

Good automation is not “send everything.”

Good automation is “move only the records that already passed the rules.”

Step 5: List Sequences And Assign Contacts

You can list sequences in a workspace like this:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences?limit=20&offset=0&statuses[]=paused" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Once your workflow selects the right sequenceID, assign contacts:

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/contacts" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "contactIds": ["contact_123", "contact_456"]
  }' 

A successful assignment returns 204 No Content.

That lack of response body is normal. Your integration should log the request, contact IDs, sequence ID, timestamp, and returned status code so you can audit what happened later.

If you are using multichannel sequences, there is also a multichannel enrollment endpoint:

curl --request POST \
  --url "https://multichannel-api.salesforge.ai/public/multichannel/workspaces/WORKSPACE_ID/sequences/123/enrollments" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "filters": {
      "contactIds": ["contact_123", "contact_456"]
    },
    "limit": 2
  }' 

Treat this as the key implementation point: legacy sequence contact assignment and multichannel enrollments are not the same endpoint family. Pick the one that matches the sequence type you are operating.

Step 6: Validate Contacts Before Launch

Start validation after contacts are assigned:

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/contacts/validation/start" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

A successful start returns 202 Accepted.

Then poll the result endpoint with a bounded retry loop:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/contacts/validation/result" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Example response shape:

{
  "status": "completed",
  "progress": 100,
  "result": {
    "safe": 87,
    "invalid": 4,
    "catch_all": 6,
    "unknown": 3
  }
} 

Then confirm only the statuses you want to send to:

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/contacts/validation/confirm" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "esps": ["gsuite", "ms365"],
    "statuses": ["safe"]
  }' 

This is where Salesforge’s deliverability controls need to be respected. Salesforge includes built-in email validation, Bounce Shield, text-only sending defaults, sender rotation, ESP matching on Growth, and unlimited warmup through Warmforge.

Those controls work best when your API workflow follows the send order.

Infrastructure first.

Warmup second.

Clean list third.

Personalized copy fourth.

Volume last.

If your integration skips validation because someone wants “full automation,” you are not improving GTM operations. You are scaling a deliverability problem.

Step 7: Assign Mailboxes, Schedules, And Sequence Status

A sequence is not ready just because contacts are attached.

Assign sending mailboxes:

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/mailboxes" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "mailboxIds": ["mailbox_123", "mailbox_456"]
  }'
Update the schedule if the workflow controls send windows:
curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/schedules" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "schedules": [
      { "weekday": 1, "fromHour": 9, "toHour": 17 },
      { "weekday": 2, "fromHour": 9, "toHour": 17 },
      { "weekday": 3, "fromHour": 9, "toHour": 17 },
      { "weekday": 4, "fromHour": 9, "toHour": 17 },
      { "weekday": 5, "fromHour": 9, "toHour": 15 }
    ]
  }'

Keep new sequences paused by default:

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/status" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "status": "paused"
  }' 

Then move to active only after review:

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/status" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "status": "active"
  }' 

For first-time workflows, I prefer create, assign, validate, pause, review, then activate.

Once the playbook is proven, you can allow trusted workflows to activate automatically.

Step 8: Create Webhooks For The Feedback Loop

Webhooks are what make the integration feel connected.

Create one webhook per event type or per workflow destination. The request requires name, type, and url. sequenceID is optional when the webhook should be scoped to a sequence.

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/integrations/webhooks" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "HubSpot positive replies",
    "type": "positive_reply",
    "url": "https://gtm.example.com/webhooks/salesforge/positive-reply",
    "sequenceID": "sequence_123"
  }' 

Example response shape:

{
  "id": "webhook_123",
  "name": "HubSpot positive replies",
  "type": "positive_reply",
  "url": "https://gtm.example.com/webhooks/salesforge/positive-reply",
  "sequenceId": "sequence_123",
  "sentCount": 0
} 

Useful webhook types include:

  • email_sent
  • email_opened
  • link_clicked
  • email_replied
  • linkedin_replied
  • contact_unsubscribed
  • email_bounced
  • positive_reply
  • negative_reply
  • label_changed
  • dnc_added
  • meeting_booked
  • meeting_completed

Do not send every event to CRM. Send the events that change ownership, routing, compliance, forecasting, or follow-up.

For most GTM teams, I would sync positive replies, negative replies, meetings, bounces, unsubscribes, DNC additions, and label changes.

A Complete HubSpot To Salesforge Workflow

Here is the end-to-end implementation I would build first.

1. HubSpot contact enters an outbound-qualified list

Trigger from HubSpot when a contact enters a list like Outbound Qualified - NA - VP Sales.

Your automation receives:

{
  "hubspotContactId": "98765",
  "email": "[email protected]",
  "firstname": "Sarah",
  "lastname": "Jones",
  "company": "Acme",
  "jobtitle": "VP Sales",
  "linkedin_url": "https://www.linkedin.com/in/sarah-jones",
  "territory": "NA",
  "lifecycle_stage": "lead",
  "owner_id": "hubspot-owner-44",
  "intent_signal": "hiring SDRs"
} 

2. Check duplicates and DNC

Before calling POST /contacts, check your own database for prior sync history.

Then call Salesforge contacts and DNC endpoints:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/contacts?limit=10&offset=0" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY"

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/dnc?limit=100&offset=0" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Because the public contacts endpoint supports filters such as tags, validation status, and not-in-sequence, I would still keep a local sync table keyed by CRM contact ID, email, workspace ID, and Salesforge contact ID.

That gives you idempotency even if a webhook or CRM trigger fires twice.

3. Map HubSpot properties to Salesforge custom variables

Use a deterministic mapping:

HubSpot Field Salesforge Field
firstname firstName
lastname lastName
email email
company company
jobtitle position
linkedin_url linkedinUrl
hubspotContactId customVars.crm_contact_id
territory customVars.territory
intent_signal customVars.intent_signal
owner_id customVars.crm_owner_id

The key rule: if the sequence copy or routing needs a value, map it before the contact enters Salesforge.

4. Create the contact

curl --request POST \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/contacts" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "firstName": "Sarah",
    "lastName": "Jones",
    "email": "[email protected]",
    "company": "Acme",
    "position": "VP Sales",
    "linkedinUrl": "https://www.linkedin.com/in/sarah-jones",
    "tags": ["hubspot", "outbound-qualified", "na"],
    "customVars": {
      "crm_contact_id": "98765",
      "crm_owner_id": "hubspot-owner-44",
      "territory": "NA",
      "intent_signal": "hiring SDRs",
      "source_system": "hubspot"
    }
  }' 

Store the returned Salesforge contact ID in your integration table and, if useful, back in HubSpot.

5. Assign the contact to a paused sequence

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/contacts" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "contactIds": ["contact_123"]
  }' 

Keep the sequence paused until the first cohort has been reviewed. This gives the campaign owner a chance to catch bad mapping, weak copy variables, wrong territory, or a sender issue before live outreach starts.

6. Validate and confirm safe contacts

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/contacts" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "contactIds": ["contact_123"]
  }' 

When validation completes, confirm the statuses your policy allows. I would start with safe only.

7. Activate after approval

curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/sequences/SEQUENCE_ID/status" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "status": "active"
  }' 

8. Receive reply webhook and update HubSpot

A positive_reply webhook should update HubSpot with a clear outcome:

{
  "event_type": "positive_reply",
  "workspace_id": "wk_123",
  "sequence_id": "sequence_123",
  "contact_id": "contact_123",
  "email": "[email protected]",
  "thread_id": "thread_123",
  "occurred_at": "2026-07-18T11:30:00Z"
} 

Your handler should then:

  • Find the local sync row by Salesforge contact ID or email.
  • Update HubSpot lifecycle stage to sales qualified or meeting requested, depending on your policy.
  • Create a task for the owner.
  • Stop or suppress duplicate follow-up workflows if the account is already in an active sales process.
  • Store the Salesforge event ID or event fingerprint so retries do not create duplicate CRM updates.

That is a real connection. Not a CSV import with nicer branding.

JavaScript Example: Create Then Assign A Contact

Here is the same basic workflow in JavaScript.

const API_BASE = "https://api.salesforge.ai/public/v2";
const API_KEY = process.env.SALESFORGE_API_KEY;

async function salesforge(path, options = {}) {
  const res = await fetch(`${API_BASE}${path}`, {
    ...options,
    headers: {
      Authorization: API_KEY,
      "Content-Type": "application/json",
      ...(options.headers || {})
    }
  });

  if (res.status === 204) return null;

  const body = await res.json().catch(() => ({}));

  if (!res.ok) {
    const message = body.message || `Salesforge API error: ${res.status}`;
    const error = new Error(message);
    error.status = res.status;
    error.body = body;
    throw error;
  }

  return body;
}

async function createAndAssignContact({ workspaceId, sequenceId, hubspotContact }) {
  const contact = await salesforge(`/workspaces/${workspaceId}/contacts`, {
    method: "POST",
    body: JSON.stringify({
      firstName: hubspotContact.firstname,
      lastName: hubspotContact.lastname,
      email: hubspotContact.email,
      company: hubspotContact.company,
      position: hubspotContact.jobtitle,
      linkedinUrl: hubspotContact.linkedin_url,
      tags: ["hubspot", "outbound-qualified"],
      customVars: {
        crm_contact_id: hubspotContact.id,
        territory: hubspotContact.territory,
        intent_signal: hubspotContact.intent_signal,
        source_system: "hubspot"
      }
    })
  });

  await salesforge(`/workspaces/${workspaceId}/sequences/${sequenceId}/contacts`, {
    method: "PUT",
    body: JSON.stringify({ contactIds: [contact.id] })
  });

  return contact;
} 

Do not run this straight from a browser. Keep the API key server-side.

Error Handling, Pagination, And Retries

The public spec shows common response patterns you should handle:

Situation Example Status What I Would Do
Bad request 400 Do not retry blindly. Log the payload, fix the field mapping, and send the request to a review queue.
Unauthorized 401 on multichannel endpoints Rotate or verify the API key, then retry only after authentication is fixed.
Forbidden 403 on multichannel endpoints Check workspace access, user permissions, and plan limits before retrying.
Not enough contact credits 402 on assign contacts Stop the job and notify RevOps or the account owner to add credits.
Not enough validation credits 402 on validation start Stop the workflow before launch. Do not bypass contact validation automatically.
Not found 404 on thread actions Re-fetch the required IDs and avoid updating the CRM with stale records.
Internal server error 500 Retry with exponential backoff, then move the request to a dead-letter queue if retries continue to fail.

Most list endpoints use limit and offset. Build pagination like this:

async function listAllWorkspaces() {
  const pageSize = 50;
  let offset = 0;
  let all = [];

  while (true) {
    const page = await salesforge(`/workspaces?limit=${pageSize}&offset=${offset}`, {
      method: "GET",
      headers: { "Content-Type": undefined }
    });

    all = all.concat(page.data || []);

    if (all.length >= page.total || !page.data?.length) break;
    offset += page.limit || pageSize;
  }

  return all;
} 

If you receive a 429 rate-limit response in production, treat it as backpressure even if your current spec view does not publish a numeric limit. Use exponential backoff, respect any retry headers returned by the API, and avoid retry storms.

For write operations, do not assume retries are safe unless your integration owns idempotency.

Production Guardrails I Would Not Skip

Here is the checklist I would use before letting a Salesforge API workflow touch live campaigns.

  • Idempotency: keep a local table keyed by source system ID, email, workspace ID, Salesforge contact ID, sequence ID, and last processed event.
  • Deduplication: check CRM, Salesforge contacts, DNC, customer lists, and active opportunities before creating or assigning contacts.
  • Partial failures: for bulk contact creation, record which contacts succeeded, which failed, and which need manual review. Do not rerun the whole batch blindly.
  • Webhook verification: verify signatures if your configured webhook setup provides them. If not, use a secret URL, IP allowlisting where possible, strict payload validation, and replay protection.
  • Duplicate webhook delivery: store an event fingerprint before updating CRM so repeated meeting_booked or positive_reply events do not create duplicate tasks.
  • Out-of-order events: define precedence. For example, contact_unsubscribed should beat a later delayed open event. A booked meeting should usually beat a delayed negative sentiment update unless a human changes the record.
  • Retry policy: retry transient 500 and 429 responses with exponential backoff. Do not retry 400 mapping errors without changing the payload.
  • Dead-letter queue: after the retry limit, send the job to a review queue with payload, endpoint, response status, and owner.
  • Key storage: keep development and production keys separate. Rotate keys when a workflow owner leaves or an automation platform changes.
  • Audit logs: log every write operation with source record, Salesforge ID, endpoint, request timestamp, status code, and operator or workflow name.
  • Rollback: know how to pause the sequence, remove contacts from future processing, and suppress CRM updates before you go live.

Tools are not the strategy. The workflow is the strategy.

The API just makes the workflow enforceable.

Where Primebox, Agent Frank, And AI Agents Fit

Primebox™ is the inbox layer. It brings email and LinkedIn replies into one place, lets you work with labels and threads, and supports AI-assisted reply work inside the inbox.

Agent Frank is different. Agent Frank is the autonomous AI SDR layer that can operate prospecting, outreach, follow-ups, and meeting booking. Co-pilot and Autopilot are Agent Frank operating modes, not Primebox label names.

That distinction matters in an API guide.

Use Primebox endpoints when your integration needs to read threads, update labels, reply to emails, reply to LinkedIn messages, or push reply outcomes back to CRM:

curl --request GET \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/threads?limit=10&offset=0&positive=true" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" 

Update a thread label:


curl --request PUT \
  --url "https://api.salesforge.ai/public/v2/workspaces/WORKSPACE_ID/mailboxes/MAILBOX_ID/threads/THREAD_ID/label" \
  --header "Authorization: YOUR_SALESFORGE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "labelId": "label_123"
  }' 

Use Agent Frank when you want autonomous execution rather than just an API-connected workflow. Because current public materials have shown inconsistent Agent Frank pricing language, I would not print a fresh exact number in this API implementation guide without product-team verification. Pricing verified: July 2026 required before final pricing reuse.

For AI agents, the direct API and Forge MCP can both matter.

Use Direct API Use Forge MCP
Server-to-server integrations AI assistant workflows
Scheduled background jobs Natural-language operation
High-volume processing Operator-in-the-loop tasks
Explicit retry and idempotency logic Exploring and executing tools through an AI client
Production pipelines Interactive analysis, drafting, and controlled actions

An API can power an AI agent. MCP can execute deterministic operations. The real difference is the interface and execution environment, not “API equals reliable” and “MCP equals judgment.”

What I Would Automate First

If I owned RevOps, I would not start with a huge automation program.

I would ship one narrow loop:

  • HubSpot list trigger.
  • DNC and duplicate checks.
  • Salesforge contact creation with custom variables.
  • Assignment to a paused sequence.
  • Validation before activation.
  • Positive, negative, bounce, unsubscribe, and meeting webhooks back to HubSpot.
  • Slack alert only when a human needs to act.

Then I would measure three things:

  • How many contacts entered Salesforge without manual CSV work.
  • How many records were blocked because of DNC, duplicates, missing fields, or validation risk.
  • How many reply outcomes made it back to CRM correctly.

That gives you an operating loop, not just an integration.

Once that works, expand into Clay, Salesforce, Pipedrive, Make, n8n, Zapier, warehouse jobs, Leadsforge, and AI-agent workflows.

When You Should Not Use The API

Do not use the API when a native integration already solves the job.

Do not use it when your CRM fields are messy and nobody owns cleanup.

Do not use it when the team has not agreed on DNC, territory, lifecycle, or ownership rules.

Do not use it to auto-launch unreviewed sequences from untrusted lead sources.

And do not use it as a shortcut around deliverability discipline.

Salesforge gives you useful controls: unlimited mailboxes, unlimited warmup through Warmforge, sender rotation, Bounce Shield, built-in email validation, ESP matching on Growth, Primebox™, multichannel sequences, API access, and integrations. But those controls still need a sane GTM process around them.

The better question is not “Can we automate this?”

The better question is “Should this action happen without a human, and what proof do we need first?”

Personalized Outbound Strategy

Get The Right Outbound Strategy In Minutes

Enter your email to get a custom plan & stack recommendation for your business

It's being carefully crafted by AI

Please check your mailbox in 5 minutes

Final Verdict: Build The Loop, Not Just The Connection

The Salesforge API is useful when it turns your GTM workflow into a connected system: qualified lead in, validated contact created, sequence assigned, replies captured, CRM updated, and the next action routed to the right owner or agent.

That is the job.

Not “API access.”

Not “sync everything.”

Not “make the campaign team faster at moving CSV files.”

If your team wants Salesforge to sit inside your GTM stack, start with one workflow and build it properly. Generate the key, find the workspace, create contacts with useful custom variables, assign them to a paused sequence, validate before launch, create webhooks for real outcomes, and add production guardrails before volume goes up.

If your reps are still stitching outbound together manually, Salesforge helps turn that work into a repeatable workflow your CRM, RevOps team, and AI agents can operate without losing control of deliverability or reply handling.

Ready to outreach
your leads with Salesforge?

You can test it out yourself - no credit card needed
Try
free
4.6 rating on G2
Add as a preferred
source on Google