When a user fills out and submits the registration form on the website, their information (name, email, opt-in status, timestamp, etc.) is sent to Salesforce Marketing Cloud (SFMC) and stored in a Data Extension — essentially a table within SFMC that holds subscriber data.
Once the data is in SFMC, the marketing team can:
The development team will handle the technical API integration on the website backend. The SFMC Admin will need to set up the Data Extension and provide API credentials. No SFMC credentials will be exposed on the website itself — all communication happens server-to-server.
What's needed from the marketing team:
What's needed from the SFMC Admin:
This document defines the technical requirements for integrating a website registration form with Salesforce Marketing Cloud (SFMC) via the REST API. The integration will create/update subscriber records in a Data Extension (DE) and optionally trigger Journey Builder campaigns in real time.
[Website Form] → [Web Backend] → [SFMC REST API] → [Data Extension] → [Journey Builder]
│
├── 1. Request OAuth2 token (cached, refreshed on expiry)
├── 2. POST registration payload to SFMC
└── 3. SFMC upserts DE row → Journey triggers (real-time or batch)
Key design decisions:
Before development begins, the SFMC Administrator must complete the following:
| Scope | Required For |
|---|---|
Data Extensions (Read and Write) |
Upserting registration records into the DE |
Journeys (Read) |
Reading journey definitions (if needed) |
List and Subscribers (Read and Write) |
Managing subscriber status |
Interactions (Execute) |
Triggering Journey Builder events (Path B only) |
Deliverables to Dev team:
Client IDClient SecretTenant-Specific Subdomain (TSD), e.g., https://[subdomain].auth.marketingcloudapis.comAccount ID (MID)Web_Registrations)| Field Name | SFMC Data Type | Primary Key? | Nullable? | Notes |
|---|---|---|---|---|
SubscriberKey |
Text (254) | Yes | No | Unique identifier. Use email address or a generated UUID. Must be unique per contact. |
EmailAddress |
EmailAddress | No | No | User's email address. Must be valid email format. |
FirstName |
Text (100) | No | Yes | User's first name |
LastName |
Text (100) | No | Yes | User's last name |
OptInStatus |
Boolean | No | No | true if user opted in to marketing communications, false otherwise. Default: false |
RegistrationTimestamp |
Date | No | No | ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ (UTC) |
Source |
Text (50) | No | Yes | Hardcoded: Website Registration |
IPAddress |
Text (45) | No | Yes | User's IP address for compliance/audit |
UserAgent |
Text (255) | No | Yes | Browser user agent string |
PageURL |
Text (500) | No | Yes | URL of the page where the form was submitted |
SubscriberKey → _SubscriberKey and EmailAddress → Email AddressDeliverable to Dev team:
Web_Registrations_2026)If the journey should trigger immediately upon registration:
Deliverable to Dev team:
EventDefinitionKey (generated by SFMC when the API Event is configured, e.g., APIEvent-xxxx-xxxx-xxxx-xxxx)SFMC uses OAuth 2.0 with the client_credentials grant type. The access token must be included as a Bearer token in all subsequent API calls.
POST https://[YOUR_SUBDOMAIN].auth.marketingcloudapis.com/v2/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"account_id": "YOUR_MID"
}
{
"access_token": "eyJhbGciOiJSUzI1NiIsIn...",
"token_type": "Bearer",
"expires_in": 1199,
"rest_instance_url": "https://[YOUR_SUBDOMAIN].rest.marketingcloudapis.com/"
}
access_token and its expiration time in server-side memory (e.g., Redis, in-memory cache, or a singleton).token_acquired_at + expires_in (minus a 60-second safety buffer).This is the recommended endpoint for single-record upserts from a registration form. It uses the DE's External Key and the primary key field value to perform an upsert (insert if new, update if exists).
POST https://[YOUR_SUBDOMAIN].rest.marketingcloudapis.com/hub/v1/dataevents/key:[DE_EXTERNAL_KEY]/rowset
Authorization: Bearer [ACCESS_TOKEN]
Content-Type: application/json
The payload uses a keys object for the primary key field(s) and a values object for all other fields. If a record with the matching key already exists, the values fields are updated. If not, a new row is inserted.
[
{
"keys": {
"SubscriberKey": "user@example.com"
},
"values": {
"EmailAddress": "user@example.com",
"FirstName": "Jane",
"LastName": "Doe",
"OptInStatus": true,
"RegistrationTimestamp": "2026-06-23T14:52:45Z",
"Source": "Website Registration",
"IPAddress": "192.168.1.1",
"UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
"PageURL": "https://example.com/register"
}
}
]
Success (200 OK):
{
"results": [
{
"rows": 1,
"errorcode": 0,
"status": "OK"
}
]
}
Validation Error (400 Bad Request):
{
"results": [
{
"rows": 0,
"errorcode": 1,
"status": "Error",
"message": "Unable to save rows for data extension..."
}
]
}
If the registration form receives high traffic or if the backend needs to decouple from SFMC response times, use the async endpoint instead:
POST https://[YOUR_SUBDOMAIN].rest.marketingcloudapis.com/data/v1/async/dataextensions/key:[DE_EXTERNAL_KEY]/rows
Authorization: Bearer [ACCESS_TOKEN]
Content-Type: application/json
Payload format differs slightly (flat object, no keys/values split):
{
"items": [
{
"SubscriberKey": "user@example.com",
"EmailAddress": "user@example.com",
"FirstName": "Jane",
"LastName": "Doe",
"OptInStatus": true,
"RegistrationTimestamp": "2026-06-23T14:52:45Z",
"Source": "Website Registration"
}
]
}
Note: The async endpoint returns a
requestIdthat can be used to check the status of the operation. It does not guarantee immediate upsert — the data is queued and processed by SFMC. Use this only if you don't need synchronous confirmation.
If the journey should fire immediately upon registration (e.g., instant welcome email, double opt-in confirmation), use the Journey Builder Event API instead of (or in addition to) the DE upsert. This endpoint injects the contact into the journey and writes the data to the journey's backing DE in a single call.
POST https://[YOUR_SUBDOMAIN].rest.marketingcloudapis.com/interaction/v1/events
Authorization: Bearer [ACCESS_TOKEN]
Content-Type: application/json
{
"ContactKey": "user@example.com",
"EventDefinitionKey": "APIEvent-xxxx-xxxx-xxxx-xxxx",
"EstablishContactKey": true,
"Data": {
"EmailAddress": "user@example.com",
"FirstName": "Jane",
"LastName": "Doe",
"OptInStatus": true,
"RegistrationTimestamp": "2026-06-23T14:52:45Z",
"Source": "Website Registration"
}
}
Field descriptions:
ContactKey — The unique identifier for the contact in SFMC. Typically the same as SubscriberKey.EventDefinitionKey — Provided by the SFMC Admin when configuring the API Event entry source.EstablishContactKey — Set to true to create the contact record if it doesn't already exist.Data — Key/value pairs matching the fields configured in the API Event entry source.Success (201 Created):
{
"eventInstanceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
| Criteria | Path A (DE Upsert Only) | Path B (Journey Event API) |
|---|---|---|
| Real-time journey trigger | No — requires batch automation | Yes — fires immediately |
| Data stored in DE | Yes | Yes (journey's backing DE) |
| Use case | Audience building, segmentation, scheduled sends | Welcome emails, double opt-in, transactional sends |
| API calls per registration | 1 (auth + upsert) | 1 (auth + event) |
| Complexity | Lower | Slightly higher (requires SFMC Admin to configure API Event) |
| Recommended for | Newsletter signups, lead capture | Immediate confirmation/welcome flows |
Recommendation: If you need an immediate welcome email or double opt-in confirmation, use Path B. If you just need to collect data for audience building and scheduled campaigns, use Path A. You can also use both — upsert to a primary DE for record-keeping (Path A) and fire the journey event (Path B) for the immediate send.
The Dev team must implement handling for the following HTTP response codes:
| HTTP Status | Meaning | Required Action |
|---|---|---|
200 / 201 |
Success | Log success, continue |
400 |
Bad Request — payload schema mismatch, missing required field, or invalid data type | Log the full request payload and response body. Do not retry — fix the payload. |
401 |
Unauthorized — token expired or invalid | Refresh the OAuth2 token and retry the request once. If the retry also returns 401, alert the team (credentials may be invalid). |
403 |
Forbidden — insufficient scopes or IP not allowlisted | Check Installed Package scopes. SFMC may require IP allowlisting for API access. |
409 |
Conflict — duplicate primary key (sync endpoint) | Typically handled by the upsert logic. If using insert-only, treat as a duplicate and skip. |
429 |
Too Many Requests — rate limit exceeded | Implement exponential backoff retry (e.g., 1s, 2s, 4s, 8s). Log and alert if retries are exhausted. |
500 / 502 / 503 |
SFMC server error | Retry with exponential backoff (up to 3 attempts). If all retries fail, queue the payload for later processing. |
retry_count and last_attempt_at field in the queue to prevent infinite retries.client_id, client_secret) in client-side code. All API calls must originate from the web backend.true/falseaccess_token or client_secret.1. User submits registration form on website
2. Web backend validates input (email format, required fields, sanitization)
3. Web backend checks cached OAuth2 token:
a. If valid → use cached token
b. If expired → request new token from SFMC auth endpoint
4. Web backend constructs JSON payload with registration data
5. Web backend sends POST to SFMC REST API:
a. Path A: /hub/v1/dataevents/key:[DE_KEY]/rowset (DE upsert)
b. Path B: /interaction/v1/events (Journey trigger)
6. SFMC processes the request:
a. Path A: Upserts row in Data Extension
b. Path B: Injects contact into Journey + writes to backing DE
7. Web backend handles response:
a. Success → log and return success to user
b. Error → retry (401/429/5xx) or queue for later (400/persistent failures)
8. SFMC Admin uses DE data for:
a. Audience segmentation and filtering
b. Journey Builder entry sources (batch automations)
c. Email send definitions
EmailAddress serve as the SubscriberKey, or will you use a generated UUID/internal user ID? This must be consistent across all SFMC integrations.