Skip to main content

Subscriber + Profile functions — Marketing Cloud AMPscript reference

AttributeValue, _subscriberKey, _emailaddress, _jobid, _messagecontext, and the rest of the context AMPscript inherits from the surrounding send. What each returns, when it's available, and the difference between Subscriber Attributes, DE columns, and local variables.

Reference·Last updated 2026-05-13·Drafted by Lira · Edited by German Medina

The Subscriber and Profile surface is how AMPscript reads the context of the current render — who the recipient is, what JobID is running, whether this is a real send or a preview, what's in the All Subscribers profile for this person. Most of the surface is special variables (the _var prefix, no parentheses) rather than function calls. Compact, predictable when you know which contexts populate which value, and a major source of confusion the first time a CloudPage AMPscript block tries to use _jobid and gets NULL.

This page is the inventory plus what each special variable returns across the render contexts (email send vs CloudPage vs Mobile vs preview).

Official syntax

%%[
  /* Special variables — no @ prefix, no parentheses */
  SET @subKey  = _subscriberKey
  SET @email   = _emailaddress
  SET @jobId   = _jobid
  SET @listId  = _listid
  SET @msgCtx  = _messagecontext     /* "Send" / "Preview" / "Test" */
  SET @userId  = _userid             /* the API/UI user who triggered (if available) */

  /* AttributeValue — reads from Subscriber Attribute profile,
     NOT from a DE column with the same name */
  SET @firstName = AttributeValue("FirstName")
  SET @region    = AttributeValue("Region")

  /* RetrieveSalesforceObjects — reads from Sales/Service Cloud
     (covered in detail in the Cloud-write reference page) */
  SET @leadStatus = Field(
    Row(
      RetrieveSalesforceObjects(
        "Lead",
        "Status",
        "Id", "=", @leadId
      ),
      1
    ),
    "Status"
  )
]%%

<p>Hello %%=v(@firstName)=%% (Subscriber Key: %%=v(@subKey)=%%).</p>

The supported surface:

| Name | What it returns | Available in | |---|---|---| | _subscriberKey | The current recipient's SubscriberKey | Email send, Mobile, CloudPage with _subscriberkey in URL | | _emailaddress | The current recipient's email | Email send only; NULL in CloudPages unless explicitly set | | _jobid | The JobID of the running Send | Email send only; NULL in preview and CloudPage | | _listid | The List or DE ID that powered the send | Email send only | | _messagecontext | "Send", "Preview", "Test", etc. | Email send + preview | | _messagetypepreference | The recipient's HTML vs Text preference | Email send only | | _userid | The API/UI user who triggered the action | Often NULL in normal sends | | JobID() | Function-form of _jobid | Same context rules | | AttributeValue(attrName) | The current Subscriber's value for the named Subscriber Attribute | Email send + CloudPage when _subscriberkey is present |

Reference:

What survives in production

_subscriberKey vs _emailaddress — pick one as your stable identifier

A sendable DE has two ways to identify the recipient:

  • SubscriberKey — Marketing Cloud's stable internal ID for the subscriber. Same person, same key across all sends.
  • EmailAddress — the email. Same person can change emails (re-key event); the address itself can change.

AMPscript exposes both via _subscriberKey and _emailaddress. The Cleon convention: always key personalization and lookups by _subscriberKey, never by _emailaddress. Email addresses change; subscriber keys are intended to be stable.

%%[
  /* DURABLE — joins on stable identifier */
  SET @segment = Lookup("master_segments", "Tier", "SubscriberKey", _subscriberKey)

  /* AT RISK — email address can have changed since the segment row was written */
  SET @segment = Lookup("master_segments", "Tier", "EmailAddress", _emailaddress)
]%%

If the sendable DE is keyed on email (rare but it happens in legacy implementations), the upstream SQL Activity should already have computed SubscriberKey as a column. AMPscript reads that column, not the raw email.

_jobid is NULL in preview and in CloudPages

Email Studio's preview doesn't run a real JobID — _jobid resolves to NULL. CloudPages don't run inside a Send, so _jobid is NULL there too. Code that depends on JobID for log writes or conditional rendering fails silently outside the live send.

%%[
  /* AT RISK — _jobid is NULL in preview; the log row writes with
     JobID = NULL, the email body renders fine, the bug surfaces
     only after the production send when audit queries can't
     correlate rows by JobID */
  InsertData(
    "de_log_engagement",
    "JobID", _jobid,
    "SubscriberKey", _subscriberKey,
    "Action", "render",
    "Ts", Now()
  )

  /* DEFENSIVE — guard against preview-time render */
  IF NOT Empty(_jobid) THEN
    /* only log on real sends */
    InsertData(
      "de_log_engagement",
      "JobID", _jobid,
      "SubscriberKey", _subscriberKey,
      "Action", "render",
      "Ts", Now()
    )
  ENDIF
]%%

The _messagecontext variable is the cleaner check: it returns "Send" for real sends and "Preview" for the preview path. Branch on _messagecontext when the rendered behavior should differ.

_messagecontext is the only reliable preview-vs-send check

%%[
  /* "Send", "Preview", "Test", etc. */
  IF _messagecontext == "Send" THEN
    /* Real production send — log + cloud-write are safe to run */
    InsertData(
      "de_log_engagement",
      "JobID", _jobid,
      "SubscriberKey", _subscriberKey,
      "Action", "render",
      "Ts", Now()
    )
  ENDIF
]%%

The values are tenant-stable enough to rely on, but not case-stable across tenants"Send" on one tenant may be "send" on another. Use Lowercase() on the comparison to be safe:

%%[
  IF Lowercase(_messagecontext) == "send" THEN
    /* portable across tenants */
  ENDIF
]%%

This is the single most useful tool for keeping preview behavior clean. The preview should never write to your log DEs, never call UpdateSingleSalesforceObject, never deliver claim codes. Wrap every side-effecting block in a _messagecontext == "Send" gate.

AttributeValue reads from the Subscriber Attribute profile, not DE columns

Reinforced from gotcha #4 because it's the single most confusing pattern in the language for new AMPscript developers:

  • AttributeValue("FirstName") reads from the All Subscribers profile's FirstName attribute. This is not the same as the FirstName column on the sendable DE.
  • %%FirstName%% (inline, no @) typically reads from the sendable DE column with that name.
  • %%=v(@FirstName)=%% reads the local variable.

When all three are named the same thing, the resolution at render time isn't always intuitive and you spend an hour wondering why personalization "looks right" but matches the wrong source.

%%[
  /* DURABLE — name the sources distinctly so the source is obvious */
  SET @subFirstName  = AttributeValue("FirstName")         /* profile */
  SET @deFirstName   = AttributeValue("DEFirstName")       /* DE column with prefix */
  SET @localFirstName = "Mariana"                          /* local */
]%%

The defense the gotchas page lists: distinct names. The defense this page adds: pick one source per email and stick with it. If your audience-build SQL Activity is computing FirstName into the sendable DE, that's the source — read %%[FirstName]%% and ignore the profile. If you want to read from the profile, name the DE column differently so confusion is impossible.

_listid is the sendable source ID, not the All Subscribers List

_listid returns the ID of the DE (or rare list) that powered the send — not the All Subscribers list. This naming is a legacy from when "Lists" were the primary audience source; today nearly every send is from a DE, and _listid returns the DE's CustomerKey/ID.

%%[
  /* @listId is the sendable DE's ID, useful for audit logging */
  SET @listId = _listid

  InsertData(
    "de_log_sends",
    "JobID", _jobid,
    "ListId", @listId,
    "Action", "render",
    "Ts", Now()
  )
]%%

Don't expect this to be human-readable in legacy MC tenants; it's an opaque ID. Use it for cross-referencing, not for displaying to subscribers.

Subscriber Attribute attribute names with spaces or special characters

The All Subscribers profile allows attribute names with spaces and unusual characters ("First Name", "Customer ID #"). AttributeValue accepts the exact name as a string, so quoting handles the spaces:

%%[
  SET @firstName = AttributeValue("First Name")        /* legacy profile with space */
  SET @customerId = AttributeValue("Customer ID #")    /* legacy with special chars */
]%%

The Cleon convention: when designing new Subscriber Attributes, use lowercase-snake-case or PascalCase without spaces or special characters. The hand-off failure: a profile attribute named "First Name" (with space) appears as a placeholder %%First Name%% in the email, which does not interpolate the same way as a no-space name. AMPscript's inline-attribute resolver has trouble with spaces. Quoted AttributeValue calls always work.

_emailaddress in a CloudPage — usually NULL

In a CloudPage rendered from a URL like /cp/preferences?_subscriberkey=abc123, _subscriberKey is populated from the URL but _emailaddress is not automatically resolved. To get the email in CloudPage context, do a Lookup against the All Subscribers list (or against your master DE):

%%[
  /* CloudPage context */
  SET @subKey = _subscriberKey                         /* from URL */
  SET @email = _emailaddress                           /* often NULL in CP */

  IF Empty(@email) THEN
    /* Fallback: look up the email by SubscriberKey */
    SET @email = Lookup("master_subscribers", "EmailAddress", "SubscriberKey", @subKey)
  ENDIF
]%%

The hand-off failure: AMPscript copied from an email template that reads _emailaddress directly gets dropped into a CloudPage, the email shows up as blank, and someone spends an hour wondering why the same code worked yesterday. The context shifted; the variable's availability shifted with it.

Quick decision

Use _subscriberKey when:

  • You need a stable per-recipient identifier. Always prefer this over _emailaddress.

Use _emailaddress when:

  • You specifically need the email address itself for personalization (the "your email on file" pattern). Otherwise, prefer the SubscriberKey + Lookup pattern.

Use _jobid when:

  • Logging or audit-trail writes that need to correlate rows to the specific send. Guard with Empty() check or _messagecontext gate.

Use _messagecontext when:

  • Gating side-effecting code (writes, claims, cloud-writes). Lowercase the comparison for portability.

Use AttributeValue when:

  • The data lives in the Subscriber Attribute profile and not on the sendable DE. Quote the name; handles spaces and special characters.

Use sendable DE column references (%%Col%% or %%[Col]%%) when:

  • The data is on the sendable DE that powered the Send. Faster than AttributeValue (no profile lookup) and clearer about the source.

Use a Lookup with _subscriberKey when:

  • The data is on a different DE keyed by SubscriberKey. Pre-shape upstream where possible to avoid per-recipient lookups.

Related

More AMPscript reference pages incoming: Cloud-write · Encoding/Hashing · Style Guide.