SfSkills Domains Docs GitHub

Home / apex / marketing-cloud-api

marketing-cloud-api

Use this skill when integrating Salesforce or external systems with Marketing Cloud Engagement REST or SOAP APIs — OAuth 2.0 authentication, journey injection via /interaction/v1/events, triggered sends, and Data Extension row operations. Trigger keywords: MC API, Marketing Cloud REST, journey injection, triggered send, fire entry event, DE row upsert, dataeventsasync, Installed Package API integration. NOT for Account Engagement (Pardot) APIs — use apex/mcae-pardot-api. NOT for choosing the integration pattern — use architect/marketing-integration-patterns.

Open the skill folder on GitHub


Marketing Cloud API

Use this skill when a task involves calling Marketing Cloud Engagement REST or SOAP APIs from Salesforce Apex, external systems, or integration middleware. It covers OAuth 2.0 authentication via Installed Package credentials, injecting contacts into journeys, firing triggered sends, and upserting rows into Data Extensions. Activate it when you see references to MC API endpoints, journey fire events, or dataeventsasync in the context.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

OAuth 2.0 Client Credentials Flow

Both the REST and SOAP Marketing Cloud APIs require an OAuth 2.0 bearer token. The token is obtained by POSTing to the tenant-specific auth endpoint:

POST https://{{subdomain}}.auth.marketingcloudapis.com/v2/token
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "{{clientId}}",
  "client_secret": "{{clientSecret}}"
}

The response contains access_token (a short-lived JWT, typically 20 minutes), token_type: Bearer, and expires_in. Cache the token and re-acquire only when it expires — do not request a new token for every API call. The token is then passed as Authorization: Bearer {{access_token}} on every subsequent request.

The Client ID and Client Secret come from the API Integration component inside an Installed Package in Marketing Cloud Setup. They are not Salesforce Connected App credentials.

Journey Injection via Fire Entry Event

To inject a contact into a Journey, POST to:

POST https://{{subdomain}}.rest.marketingcloudapis.com/interaction/v1/events
Authorization: Bearer {{access_token}}
Content-Type: application/json

{
  "ContactKey": "subscriber@example.com",
  "EventDefinitionKey": "APIEvent-abc123",
  "Data": {
    "FirstName": "Jane",
    "AccountTier": "Gold"
  }
}

Critical: The field names inside Data must exactly match the field names configured on the journey's entry source schema — including case. If a field name is wrong or missing, the API returns HTTP 201 (Created) but the contact is silently dropped from the journey with no error. There is no retry or error callback.

EventDefinitionKey is found in Journey Builder under the journey's entry source settings, not the Journey's own key. These are different values.

Triggered Sends

Triggered sends require a three-step lifecycle:

  1. Create a TriggeredSendDefinition via SOAP API or Marketing Cloud Setup UI, specifying the email, subscriber list, and ExternalKey.
  2. Start the definition (status must be Active / Running). Sending against a definition in Draft status returns an error.
  3. Fire the send via REST:
    POST https://{{subdomain}}.rest.marketingcloudapis.com/messaging/v1/messageDefinitionSends/key:{ExternalKey}/send

    with a payload containing the subscriber's To.Address and To.SubscriberKey, plus any profile attribute substitutions.

Data Extension Row Operations

Two patterns exist for writing rows to a Data Extension:


Common Patterns

Pattern: Apex Callout for Journey Injection

When to use: Salesforce Apex code needs to fire a contact into a Marketing Cloud journey after a record change (e.g., Opportunity stage change triggers onboarding journey).

How it works:

  1. Store Client ID, Client Secret, and subdomain in a Custom Metadata Type or Named Credential (never hardcode).
  2. Make an Apex HttpRequest to the token endpoint; parse the access_token from the JSON response.
  3. Build the journey injection payload with the correct EventDefinitionKey and Data object using the exact field names from the entry source schema.
  4. POST to /interaction/v1/events with the bearer token.
  5. Check the HTTP response code. A 201 means the request was accepted — not that the contact entered the journey. Log the response body for debugging.

Why not a simple callout without token caching: Each token request counts against API call limits. In a trigger or batch context, acquiring a new token per record will exhaust limits quickly.

Pattern: Async DE Row Upsert for Bulk Sync

When to use: A nightly batch or platform event processor needs to sync hundreds or thousands of Salesforce records into a Marketing Cloud Data Extension.

How it works:

  1. Batch records into chunks of up to 2,000 rows per request (check current limit in official docs).
  2. POST each chunk to dataeventsasync endpoint and store the returned requestId.
  3. After all chunks are submitted, poll each requestId until status is Complete or Error.
  4. Log errors and implement retry logic only for Error status responses.

Why not the synchronous endpoint: The sync endpoint blocks the Apex thread and can time out on large datasets. The async endpoint decouples submission from confirmation and is the documented pattern for bulk operations.


Decision Guidance

SituationRecommended ApproachReason
New REST vs SOAP for new integrationUse REST APIREST is the documented preferred approach; simpler, JSON-native, broader support
Sending to < 100 DE rows in real-timeSync upsert /hub/v1/dataevents/.../rowsetImmediate confirmation; simpler error handling
Sending > 100 DE rows or batch jobAsync /hub/v1/dataeventsasync/.../rowsetAvoids throttling; decouples submission from confirmation
Contact must enter a JourneyPOST /interaction/v1/events with exact field namesJourney injection is the only supported entry mechanism for API-triggered journeys
Sending a transactional emailUse Triggered Send via /messaging/v1/messageDefinitionSends/...Designed for 1:1 transactional sends; journeys add unnecessary overhead
AuthenticationOAuth 2.0 client credentials from Installed PackageLegacy Fuel credentials are deprecated; client credentials is the only current standard

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Confirm credentials and endpoints: Retrieve the Client ID, Client Secret, and tenant-specific subdomain from the Installed Package API Integration component. Verify the subdomain format is {{MID}}.auth.marketingcloudapis.com — not a generic URL.
  2. Acquire an OAuth 2.0 token: POST to the /v2/token endpoint with grant_type=client_credentials. Parse and cache access_token and expires_in. Implement token refresh logic so the token is re-acquired before expiry, not on every call.
  3. Identify the target resource: For journeys, locate the EventDefinitionKey from the entry source settings (not the journey key). For triggered sends, confirm the TriggeredSendDefinition exists and is in Active status. For DEs, confirm the DE ExternalKey and retrieve the field schema.
  4. Build the request payload: Match field names exactly to the target resource schema. For journey injection, every field in Data must match entry source field names case-sensitively. For DE row operations, include all required fields or upsert will fail silently.
  5. Make the API call and handle the response: A 201 for journey injection means accepted, not confirmed entry. A requestId from async DE insert requires polling. Log the full response body — Marketing Cloud error messages are in the body, not always in the status code.
  6. Validate end-to-end: In Marketing Cloud, check Journey Builder contact history or DE row count to confirm records are processed, not just accepted.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. Journey injection silently drops contacts on field name mismatch — If any field name in the Data object does not exactly match the entry source schema (including case), the API returns HTTP 201 but the contact never enters the journey. There is no error message and no retry. This is the most common cause of "contacts not entering the journey" bugs.
  2. TriggeredSendDefinition must be Started before firing — Creating the definition is not enough. The definition must be explicitly started (status Active). Firing a send against a Draft definition returns an error, but the error message is not always obvious that status is the cause.
  3. Tenant-specific subdomain is mandatory — generic endpoints fail — Marketing Cloud REST API endpoints are always tenant-specific: https://{{subdomain}}.rest.marketingcloudapis.com. Using a generic URL or the wrong tenant subdomain results in authentication failures or 404 errors that look like network issues.

Output Artifacts

ArtifactDescription
OAuth 2.0 token acquisition snippetApex HttpRequest or REST client code to obtain and cache a bearer token from the MC token endpoint
Journey injection payloadJSON request body with EventDefinitionKey and Data object matching entry source schema
Triggered send sequenceThree-step setup: create definition, start it, fire the send POST request
DE row upsert requestSync or async POST payload for Data Extension row operations with requestId polling for async

Package files