Funciones de Cloud-write — referencia de Marketing Cloud AMPscript
El puente de AMPscript hacia Sales/Service Cloud — UpdateSingleSalesforceObject, CreateSalesforceObject, RetrieveSalesforceObjects. La superficie de mayores stakes del lenguaje: los writes son inline, devuelven 1/0 sin tirar, y pueden pegar a registros CRM de producción reales desde un preview si te olvidás el gate de messagecontext.
Las funciones de Cloud-write son la superficie de mayores stakes en AMPscript. Cruzan Marketing Cloud Connect (MC Connect) hacia Sales/Service Cloud y crean, leen, o actualizan registros ahí. Tres cosas hacen esto peligroso: los writes son síncronos e inline con el render del email, devuelven 1/0 sin tirar en cualquier falla (permiso, validación, campo faltante), y pueden pegar a registros CRM de producción en vivo desde un preview si falta el gate _messagecontext. Un solo gate olvidado más un miembro del equipo curioso previewando un email es suficiente para actualizar Opportunities, Cases, o Leads reales con data de prueba.
Esta página es el inventario más los patrones que mantienen estas llamadas seguras. La disciplina "loggear cada resultado" de la página de Data Extension aplica acá todavía más estrictamente — una Cloud-write fallida es invisible sin el recibo.
Sintaxis oficial
%%[
/* UpdateSingleSalesforceObject — actualiza un registro. Devuelve 1/0. */
SET @result = UpdateSingleSalesforceObject(
"Lead", /* tipo de objeto SF */
@leadId, /* Id del registro SF */
"Status", "Engaged", /* par campo-valor */
"LastEmailOpened__c", Now() /* otro par */
)
/* CreateSalesforceObject — crea un registro.
El 2do arg es el *conteo de pares de campos* — fácil de contar mal. */
SET @newId = CreateSalesforceObject(
"Task",
4, /* 4 pares de campos siguen */
"WhoId", @leadId,
"Subject", "Followed email click",
"Status", "Not Started",
"ActivityDate", Now()
)
/* RetrieveSalesforceObjects — lee registros. Devuelve un rowset
como LookupRows; aplican Field/Row/RowCount. */
SET @rows = RetrieveSalesforceObjects(
"Lead", /* tipo de objeto SF */
"Id, Status, Owner.Name", /* lista de campos separada por comas */
"Email", /* campo de filtro */
"=", /* operador */
@subscriberEmail /* valor de filtro */
)
SET @n = RowCount(@rows)
IF @n > 0 THEN
SET @lead = Row(@rows, 1)
SET @leadId = Field(@lead, "Id")
SET @status = Field(@lead, "Status")
ENDIF
]%%El set soportado:
| Función | Propósito | Notas |
|---|---|---|
| UpdateSingleSalesforceObject(type, id, f1, v1, f2, v2, ...) | Actualiza un registro SF | Devuelve 1/0; silencioso en falla de permiso/validación |
| CreateSalesforceObject(type, nFields, f1, v1, f2, v2, ...) | Crea un registro SF | El 2do arg es el conteo de pares de campos; devuelve el Id del nuevo registro en éxito, NULL/vacío en falla |
| RetrieveSalesforceObjects(type, fieldList, filterField, op, filterVal) | Lee registros SF | Devuelve rowset; RowCount = 0 significa sin match o read fallido (sin distinción) |
| RetrieveSalesforceObjects(type, fieldList, ff1, op1, fv1, ff2, op2, fv2, ...) | Variante multi-filtro | Pares de tripletas de filtro; misma forma de retorno |
Referencia:
- AMPscript Guide — referencia de funciones (sección Salesforce) ↗
- Salesforce Help — Marketing Cloud Connect ↗
Lo que sobrevive en producción
_messagecontext == "Send" es obligatorio antes de cualquier Cloud-write
Una llamada a UpdateSingleSalesforceObject adentro del cuerpo de un email corre cada vez que el email se renderea — incluyendo en el preview de Email Studio. Sin el gate, cada render de preview dispara un write real contra registros de Salesforce de producción. Lo mismo aplica a los test sends.
%%[
IF Lowercase(_messagecontext) == "send" THEN
/* Solo dispara en sends de producción reales */
SET @result = UpdateSingleSalesforceObject(
"Lead", @leadId,
"Status", "Engaged",
"LastEmailOpened__c", Now()
)
InsertData(
"de_log_sf_writes",
"JobID", _jobid,
"SubscriberKey", _subscriberKey,
"SfObject", "Lead",
"SfId", @leadId,
"Operation", "Status=Engaged",
"Result", @result,
"Ts", Now()
)
ENDIF
]%%La falla del hand-off: alguien saca el gate "para testear en preview", se olvida de ponerlo de vuelta, sale el email. Cada preview de QA después de ese punto silenciosamente sobrescribe Statuses de Lead reales con el valor de prueba. El costo lo pagan los equipos de ventas y customer success que ven data corrupta en CRM sin un disparador claro.
La convención Cleon: el gate _messagecontext va alrededor del bloque entero con side-effects, no alrededor de llamadas individuales de write. El gate es el perímetro; los writes están adentro.
UpdateSingleSalesforceObject devuelve 1/0 — y 0 significa muchas cosas
Un valor de retorno 0 puede significar:
- El registro no existe (Id equivocado, o borrado desde que se armó la audiencia)
- El API user no tiene permiso para actualizar el objeto
- El API user no tiene permiso para actualizar uno de los campos específicos
- Una regla de validación se disparó en el lado de SF
- El registro está lockeado (proceso de aprobación, sharing, record-locking)
- MC Connect perdió su token de sesión a mitad del send
- Field-level security bloquea el campo en el perfil de este user
- El valor no matchea el tipo de data del campo (ej. valor de picklist que no está en la lista)
La función devuelve 0 para todos sin mensaje distintivo. El log es el único diagnóstico.
%%[
IF Lowercase(_messagecontext) == "send" THEN
SET @result = UpdateSingleSalesforceObject(
"Lead", @leadId,
"Status", "Engaged"
)
/* DURABLE — loggeá cada llamada con suficiente contexto para diagnosticar después */
InsertData(
"de_log_sf_writes",
"JobID", _jobid,
"SubscriberKey", _subscriberKey,
"SfObject", "Lead",
"SfId", @leadId,
"Operation", "Status=Engaged",
"Result", @result,
"Ts", Now()
)
ENDIF
]%%Auditá de_log_sf_writes después de cada Send: SELECT COUNT(*) WHERE Result = 0 GROUP BY Operation te dice qué writes están fallando y cuántos. Sin el log, la única señal es que ventas note que el data de CRM está desactualizada.
El argumento de field-count de CreateSalesforceObject es una trampa
El 2do argumento es "cuántos pares campo-valor siguen". Contalo mal y AMPscript silenciosamente trunca la lista de input o devuelve NULL sin error útil.
%%[
/* EN RIESGO — el 4 está mal; hay solo 3 pares.
AMPscript puede silenciosamente saltarse el último par, puede devolver NULL,
puede escribir basura. El comportamiento depende del tenant. */
SET @newId = CreateSalesforceObject(
"Task",
4, /* ← mal; solo 3 pares siguen */
"WhoId", @leadId,
"Subject", "Followed email click",
"Status", "Not Started"
)
/* DURABLE — contá los pares visualmente con formato uno-por-línea
y alinealos para que el conteo sea verificable en code review */
SET @newId = CreateSalesforceObject(
"Task",
4, /* 4 pares ↓ */
"WhoId", @leadId,
"Subject", "Followed email click",
"Status", "Not Started",
"ActivityDate", Now()
)
IF Empty(@newId) THEN
/* Creación falló — loggear y no confiar en @newId */
InsertData(
"de_log_sf_writes",
"JobID", _jobid,
"SfObject", "Task",
"Operation", "Create",
"Result", 0,
"Ts", Now()
)
ENDIF
]%%La convención Cleon: formato vertical un-par-por-línea con un comentario contándolos al tope. Code review detecta un mismatch inmediatamente.
RetrieveSalesforceObjects está paginado por debajo — pero el page size es opaco
RetrieveSalesforceObjects devuelve un rowset, pero cuántas filas tiene ese rowset depende del query plan de SF, del API limit, y del tenant. No hay un row cap documentado para los reads de Cloud de AMPscript como LookupRows tiene 2000 — pero asumí que puede ocurrir una truncación similar en silencio.
%%[
SET @rows = RetrieveSalesforceObjects(
"Opportunity",
"Id, Amount, StageName",
"AccountId", "=", @accountId
)
SET @n = RowCount(@rows)
/* Para cuentas de alto valor con muchas opportunities, @n puede estar
truncado. Auditá a escala antes de apoyarte en result sets completos. */
/* La defensa matchea DE: formateá el data upstream cuando la audiencia
tiene muchas filas. Una SQL Activity que joinea audiencia MC × data SF
vía los DEs sincronizados corre una vez, produce el result set,
y AMPscript lee una columna de DE pre-formateada. */
]%%Si el trabajo requiere leer más de ~100 registros SF por destinatario, la herramienta correcta no es AMPscript — es SQL Activity contra los DEs sincronizados (los DEs que Marketing Cloud Connect popula desde el data de SF). Los DEs sincronizados son grandes, queryables con SQL, y no tienen el costo per-destinatario.
MC Connect tiene que estar configurado antes de cualquier llamada a función Cloud
Estas funciones requieren que el tenant tenga Marketing Cloud Connect instalado y configurado contra una org Salesforce específica. Sin eso:
UpdateSingleSalesforceObjectdevuelve0inmediatamente sin intentar llamada a SFCreateSalesforceObjectdevuelve NULLRetrieveSalesforceObjectsdevuelve un rowset vacío
Las funciones no te dicen "MC Connect no está configurado" — simplemente se comportan como si el lado de SF aceptó-y-rechazó cada llamada. Si estás heredando un tenant MC y un bloque de Cloud-write previamente funcionando de repente devuelve todos 0, chequeá si MC Connect sigue autenticado contra la org de SF antes de debuggear el script.
Escenarios cross-BU / cross-org cambian qué org SF recibe el write
Si el tenant MC tiene múltiples Business Units conectadas a distintas orgs Salesforce, la función de Cloud-write dispara contra la org conectada a la BU que está corriendo el send. Esto suele estar bien, pero la forma de falla: un email movido de la BU A (conectada a Sandbox) a la BU B (conectada a Producción) silenciosamente cambia cada Cloud-write de CRM sandbox a CRM producción. No hay warning en send time.
%%[
/* La función usa MC Connect para la org conectada de la BU actual.
Si migraste el email entre BUs, auditá los writes después
del primer send en la BU nueva antes de confiar en el patrón. */
]%%La convención Cleon: antes de promover cualquier email con Cloud-write entre Business Units, documentá la org SF conectada explícitamente en el DE de run-history y verificá que el primer send de producción escribió a la org esperada. El costo de un mismatch es desarmar updates malos en CRM de producción.
Los rate limits son una restricción real a escala
Los API limits de Salesforce aplican a las llamadas de Cloud-write de AMPscript. Un send a 50.000 destinatarios con un UpdateSingleSalesforceObject por destinatario son 50.000 llamadas a la API de SF — bien adentro del territorio de rate-limit para muchos tenants. La función no encola, no reintenta, no se desacelera. Las llamadas pasando el rate limit devuelven 0.
%%[
/* Para sends de alto volumen, pre-computá los writes en un solo
update de DE sincronizado upstream, O batcheá vía el tooling
bulk-API-friendly de Salesforce. Los Cloud-writes de AMPscript
son para patrones de bajo volumen y altos stakes (emails
transaccionales, follow-ups personalizados), no para updates en bulk. */
]%%Si tu send es de más de algunos miles de destinatarios y cada uno necesita un Cloud-write, la arquitectura está mal — mové el write a una SSJS Script Activity que batchee, o a una SQL Activity contra el DE sincronizado que actualiza el objeto SF vía el path bulk de MC Connect.
Decisión rápida
Usá UpdateSingleSalesforceObject cuando:
- Actualizás un registro SF por destinatario en un send de bajo volumen (transaccional, triggered, audiencia sub-1000). Siempre adentro de un gate
_messagecontext == "Send", siempre loggeado.
Usá CreateSalesforceObject cuando:
- Creás un registro SF por evento de send (un Task, una Note, una fila de log de evento). Contá los pares de campos en formato vertical. Gate. Log.
Usá RetrieveSalesforceObjects cuando:
- Leés un número chico de registros SF por destinatario (la última Opportunity, el Owner de un Lead). Para lecturas más grandes, usá los DEs sincronizados vía SQL Activity upstream.
Mové a SSJS Script Activity / SQL Activity en su lugar cuando:
- La audiencia es de más de algunos miles y cada destinatario dispara un write. Los rate limits van a frenarte.
- El mismo update SF aplica a cada destinatario — pre-computá en SQL contra el DE sincronizado, corré como un solo paso de Activity antes del send.
- Necesitás lógica de reintento o semántica de batch. Los Cloud-writes de AMPscript son fire-and-(maybe-)forget.
- Estás leyendo muchos registros SF por destinatario. Los DEs sincronizados están diseñados para eso.
Relacionado
- Basics — superficie del lenguaje soportada
- Funciones de Subscriber + Profile —
_messagecontextes el gate que cada Cloud-write necesita - Funciones de Data Extension —
InsertDatapara loggear resultados de Cloud-write ade_log_sf_writes - Funciones de validación —
Empty()para guardar el resultado deCreateSalesforceObject(devuelve NULL en falla) - Gotchas de MC AMPscript — ver #10 (patrón de falla silenciosa de Cloud-write, la forma de falla que esta página diagnostica)
- MC SSJS — WSProxy — el equivalente SSJS para operaciones SF en batch; lo que agarrás cuando los Cloud-writes de AMPscript pegan los rate limits
Más páginas de referencia AMPscript en camino: Encoding/Hashing · Style Guide.