Skip to main content
This tutorial walks through a full multichannel-sequence setup for a single workspace. By the end of the flow, you will have:
  1. Created three leads with different channel availability.
  2. Created a multichannel sequence in draft state.
  3. Added a LinkedIn branch and an email branch.
  4. Optionally validated leads before enrollment.
  5. Enrolled leads into the sequence.
  6. Launched the sequence.
This tutorial assumes your sender profile already exists and is ready to use. The guide still covers how to list sender profiles and assign one to the sequence.
This is a two-service workflow.
  • Use the Salesforge public API to create leads in the workspace.
  • Use the multichannel public API to create the sequence, add nodes, validate leads, enroll them, and launch the sequence.
The endpoint paths below are shown as they appear in the public API references.

Prerequisites

Prepare these values before you start:
  • WORKSPACE_ID
  • SALESFORGE_API_KEY
  • SENDER_PROFILE_ID
  • a base URL for the Salesforge public API
  • a base URL for the multichannel public API
Salesforge public API and multichannel public API use the same API key. Use SALESFORGE_API_KEY for requests to both services. Both APIs use bearer authentication.
Authorization: Bearer YOUR_API_KEY
For the shared authentication pattern, see Authentication.

Step 1: Create leads in the workspace

Start by creating the leads you want to target. For this guide, create three lead shapes:
  • one lead with email only,
  • one lead with LinkedIn only,
  • one lead with both email and LinkedIn.
Use the Salesforge public API bulk-create endpoint so you can create the full sample set in one request. POST /workspaces/{workspaceID}/contacts/bulk
The Salesforge public API uses contacts in endpoint paths and response field names. In this guide, those same records are referred to as leads to keep the sequencing terminology consistent.
{
  "contacts": [
    {
      "firstName": "Alicia",
      "lastName": "North",
      "email": "alicia.north@example.com",
      "company": "Northwind Labs",
      "position": "Revenue Operations Manager",
      "tags": ["guide-multichannel", "email-only"],
      "customVars": {
        "segment": "SMB",
        "industry": "Software"
      }
    },
    {
      "firstName": "Bruno",
      "lastName": "Vale",
      "linkedinUrl": "https://www.linkedin.com/in/bruno-vale",
      "company": "Atlas Advisory",
      "position": "Founder",
      "tags": ["guide-multichannel", "linkedin-only"],
      "customVars": {
        "segment": "Founder-led"
      }
    },
    {
      "firstName": "Camila",
      "lastName": "Stone",
      "email": "camila.stone@example.com",
      "linkedinUrl": "https://www.linkedin.com/in/camila-stone",
      "company": "Signal Peak",
      "position": "VP Sales",
      "tags": ["guide-multichannel", "omnichannel"],
      "customVars": {
        "segment": "Mid-market",
        "priority": "High"
      }
    }
  ]
}
This request uses the public lead-creation rules exposed by the contact endpoint:
  • firstName is required.
  • At least one of email or linkedinUrl must be present.
  • Each lead must include at least one tag through tags or tagIds.
  • customVars is optional, but it cannot be an empty object.
The response returns created leads under the contacts field, with fields such as id, email, linkedinUrl, tags, and customVars. The action examples later in this guide use {{segment}} in message content, so make sure each lead you plan to enroll has that custom variable populated. Store the returned IDs for later steps. In this tutorial, refer to them as:
  • EMAIL_ONLY_LEAD_ID
  • LINKEDIN_ONLY_LEAD_ID
  • OMNICHANNEL_LEAD_ID
If you want to confirm the leads are available in the workspace, you can also query the workspace contact-list endpoint. GET /workspaces/{workspaceID}/contacts

Step 2: Discover the available multichannel actions and conditions

Do not hardcode action or condition IDs in client code. Use the reference endpoints first, then resolve the IDs you need from the response.

List email actions

GET /multichannel/actions?channel=email The action catalog includes a name, channel, branching type, and description. For an email step, the action you usually want is the one whose name is send_email.

List LinkedIn actions

GET /multichannel/actions?channel=linkedin Common LinkedIn actions include these names:
  • li_connection_request
  • li_send_message
  • li_send_inmail
  • li_view_profile
  • li_withdraw_connection_request
  • li_like_latest_post
  • li_follow_profile
For this tutorial, resolve the id for li_send_message and store it as LINKEDIN_MESSAGE_ACTION_ID.

List available conditions

GET /multichannel/conditions Use this endpoint to resolve the condition IDs you want to use in the graph. For the sample flow below, resolve the condition whose name is has_linkedin_url and store its ID as HAS_LINKEDIN_URL_CONDITION_ID. Other useful conditions you may see in the same catalog include:
  • has_email_address
  • request_accepted_within_days
  • li_is_already_connected
  • li_contact_replied_within_days
  • check_email_validation_status

Step 3: Create the multichannel sequence

Create the sequence in draft state first. POST /multichannel/workspaces/{workspaceID}/sequences
{
  "name": "Guide - LinkedIn then Email",
  "description": "A sample multichannel sequence that routes leads into LinkedIn or email outreach.",
  "timezone": "America/New_York",
  "settings": {
    "openTrackingEnabled": true,
    "plainTextEmailsEnabled": true,
    "optOutLinkEnabled": true,
    "optOutLinkText": "Unsubscribe",
    "trackOpportunitiesEnabled": false
  }
}
The create response returns the new sequence with fields such as:
  • id
  • status
  • timezone
  • name
  • description
  • settings
Store the returned id as SEQUENCE_ID.
espMatchingEnabled is part of the public settings model, but enabling it can be feature-gated by workspace plan. Keep it omitted or false unless you know the workspace supports it.
If you need to refine sequence metadata after creation, use the update endpoint. PATCH /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}

Step 4: Set an explicit schedule

New sequences receive a default schedule, but production integrations should normally set the schedule explicitly. PUT /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/schedule
{
  "timezone": "America/New_York",
  "schedule": {
    "monday": { "enabled": true, "from": 9, "to": 17 },
    "tuesday": { "enabled": true, "from": 9, "to": 17 },
    "wednesday": { "enabled": true, "from": 9, "to": 17 },
    "thursday": { "enabled": true, "from": 9, "to": 17 },
    "friday": { "enabled": true, "from": 9, "to": 17 },
    "saturday": { "enabled": false },
    "sunday": { "enabled": false }
  }
}
The schedule model uses per-day enabled, from, and to values, where hours are integers from 0 to 23 and to must be greater than from. To verify the schedule that is now attached to the sequence, call: GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/schedule

Step 5: Assign an existing sender profile

The sender profile is the execution identity for the sequence. It is where mailbox and LinkedIn execution context come together. First, list sender profiles for the workspace if you need to confirm the correct profile ID. GET /multichannel/workspaces/{workspaceID}/sender-profiles Then assign the sender profile to the sequence. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/sender-profiles
{
  "senderProfileIds": [123]
}
Replace 123 with your actual sender profile ID or the SENDER_PROFILE_ID you already prepared. To verify the assignment, call: GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/sender-profiles

Step 6: Get the root branch before adding nodes

When a multichannel sequence is created, the root node is created automatically. You do not create that node yourself. Before you can add an action or condition, you need the branch ID that leaves the root node. GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/branches Each branch record includes:
  • id
  • fromNodeId
  • toNodeId
  • name
  • description
Store the branch ID that starts from the root node as ROOT_BRANCH_ID.

Desired sequence shape

This is the sequence you are building in the next steps. The root node already exists when the sequence is created, then a condition node routes leads by LinkedIn availability: leads with a LinkedIn identity go to the LinkedIn message branch, while email-only leads go to the email branch.

Step 7: Add the first condition node

This tutorial uses one condition node first so the sequence can route leads by channel availability. Leads with a LinkedIn URL will go down the LinkedIn path. Leads without one will go down the email path. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes/conditions
{
  "branchId": 1001,
  "conditionId": 12,
  "minutesToWait": 0,
  "distributionStrategy": "equal"
}
Replace:
  • 1001 with ROOT_BRANCH_ID
  • 12 with HAS_LINKEDIN_URL_CONDITION_ID
The created node response returns the condition node itself. After that, list branches again. GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/branches The condition node creates outgoing branches that you can identify by name, typically yes and no. Store them as:
  • LINKEDIN_BRANCH_ID for the yes branch
  • EMAIL_BRANCH_ID for the no branch

Step 8: Add a LinkedIn action and an email action

Now create one action node on each branch.

LinkedIn message action

Use the branch for leads that do have a LinkedIn URL. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes/actions
{
  "branchId": 2001,
  "actionId": 31,
  "waitDays": 0,
  "distributionStrategy": "equal",
  "variants": [
    {
      "metadata": {
        "name": "LinkedIn intro",
        "message": "Hi {{first_name}}, I work with {{segment}} teams that want more control over multichannel outbound. Open to a short conversation?",
        "subject": ""
      },
      "exposureInPercentage": 100,
      "isEnabled": true
    }
  ]
}
Replace:
  • 2001 with LINKEDIN_BRANCH_ID
  • 31 with LINKEDIN_MESSAGE_ACTION_ID

Email action

Use the branch for leads that do not have a LinkedIn URL. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes/actions
{
  "branchId": 2002,
  "actionId": 14,
  "waitDays": 1,
  "distributionStrategy": "equal",
  "variants": [
    {
      "metadata": {
        "name": "Email follow-up",
        "subject": "Quick intro for {{company}}",
        "message": "Hi {{first_name}}, I wanted to share a simple way {{segment}} teams use multichannel workflows without splitting leads across separate systems.",
        "allowed_validation_statuses": ["safe", "catch_all"]
      },
      "exposureInPercentage": 100,
      "isEnabled": true
    }
  ]
}
Replace:
  • 2002 with EMAIL_BRANCH_ID
  • 14 with the action ID whose name is send_email
This gives the sequence both channels while still handling mixed lead shapes in one graph. If you want a lead to receive LinkedIn first and email later in the same branch, keep extending the graph by listing branches again after node creation, then creating new actions on the downstream branch IDs returned by the API.

Step 9: Inspect the graph you just created

Before you validate or enroll leads, inspect the sequence structure. Useful inspection endpoints are:
  • GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}
  • GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes
  • GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes/{nodeID}
  • GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/branches
These responses let you verify:
  • the sequence is still in draft status,
  • the correct sender profile is attached,
  • the nodes are present with the right actionId or conditionId,
  • the branch IDs and yes or no routing look correct.

Step 10: Optionally run validation before enrollment

Validation is optional, but it is useful if you want to enroll leads based on email quality or re-use a validation run as part of your selection logic.
Validation in this flow is email-only. Use it for leads that have an email address, and add LinkedIn-only leads through a separate enrollment filter instead of putting them in the validation run.
Start the validation run first. POST /multichannel/workspaces/{workspaceID}/validations
{
  "filters": {
    "leadIds": [
      "EMAIL_ONLY_LEAD_ID",
      "OMNICHANNEL_LEAD_ID"
    ]
  },
  "limit": 2
}
The create response returns validationJobID. Use that value as the run identifier in later steps. To inspect the results, call: GET /multichannel/workspaces/{workspaceID}/validations/{runID}/results Validation filters can be broader than leadIds. The API also supports filters such as:
  • tagIds
  • esps
  • validationStatuses
  • customVars
  • searchQuery
  • hasValidLinkedIn
  • hasEmail
  • excludeContacted
That makes validation a reusable pre-enrollment selection layer rather than only a one-off email hygiene check.

Step 11: Enroll leads into the sequence

The multichannel public API supports several enrollment styles.

Option A: Enroll explicit lead IDs

Use this when you already know the leads you want in the sequence. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/enrollments
{
  "filters": {
    "leadIds": [
      "EMAIL_ONLY_LEAD_ID",
      "LINKEDIN_ONLY_LEAD_ID",
      "OMNICHANNEL_LEAD_ID"
    ]
  },
  "limit": 3
}
This is the most explicit enrollment mode, and it is a good fit when another system already selected the audience.

Option B: Enroll from a validation run

Use this when you want the validation job to become the source for enrollment. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/enrollments
{
  "filters": {
    "validationRunId": "VALIDATION_JOB_ID",
    "validationStatuses": ["safe", "catch_all"]
  },
  "limit": 50
}
This pattern is useful when you want the validation run to supply the email-capable audience for the sequence.

Option C: Add LinkedIn-only leads with another enrollment filter

Use this as a second enrollment call when you also want to add the LinkedIn-only segment to the same sequence. POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/enrollments
{
  "filters": {
    "tagIds": ["LINKEDIN_ONLY_TAG_ID"],
    "hasValidLinkedIn": true
  },
  "limit": 50
}
Use the tag ID that represents your linkedin-only segment in the workspace. This keeps the LinkedIn-only audience out of the email-validation step while still adding it through the same enrollment endpoint. The enrollment filters support these selectors:
  • leadIds
  • tagIds
  • esps
  • validationStatuses
  • validationRunId
  • hasValidLinkedIn
  • hasEmail
  • limit
If you combine validationRunId with explicit leadIds, the service resolves the validation run and intersects it with the lead ID list instead of treating those filters as unrelated sets. In practice, that means a common pattern is: first enroll the validated email-capable leads with validationRunId, then make a second enrollment call with a different filter such as tagIds to add the LinkedIn-only audience. The enrollment response returns the leadIds that were actually enrolled.

Step 12: Verify enrollments and launch the sequence

Before launch, re-check the sequence details. GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID} The detailed response includes:
  • sequence
  • nodes
  • branches
  • activeEnrollmentCount
Once the graph, schedule, sender profile, and enrollments look correct, launch the sequence. PATCH /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/launch This endpoint does not require a request body. A successful response returns the sequence in its launched state, with status set to active.
Treat launch as the point where the sequence graph becomes locked. In the multichannel-api service, launched sequences cannot be structurally edited: creating nodes, deleting nodes, or rewiring branches is blocked after launch.Operational updates still remain available through public endpoints. The code continues to allow adding new enrollments after launch, and schedule updates also remain available. Public update endpoints for sequence metadata/settings and sender-profile assignment are still exposed as well, so the important restriction here is specifically on changing the workflow structure after launch.
The response returns the sequence in its launched state. That is the point where the sequence can begin active execution.

Step 13: Know the follow-up endpoints you will use next

After the first launch, these are the endpoints you will usually use operationally:
  • GET /multichannel/workspaces/{workspaceID}/sequences to list sequences
  • GET /multichannel/workspaces/{workspaceID}/sequences/{sequenceID} to inspect one sequence in detail
  • PATCH /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/status to pause or resume a sequence
  • POST /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/enrollments/remove to remove leads by filter
  • PATCH /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes/actions/{nodeID} to update an action node
  • DELETE /multichannel/workspaces/{workspaceID}/sequences/{sequenceID}/nodes/{nodeID} to remove a node
  • DELETE /multichannel/workspaces/{workspaceID}/sequences/{sequenceID} to delete the sequence
Those are maintenance endpoints. The create-to-launch flow stays the same: create leads, resolve action and condition IDs, create the sequence, set schedule, assign sender profile, build nodes, optionally validate, enroll, inspect, and launch.