SHA256
Returns the SHA-256 hash of the input value. Runtime-proven on a live Marketing Cloud Engagement CloudPage — including the fact that the bytes hashed are the UTF-8 encoding of the input, and that an unrecognised encoding name aborts the page.
Syntax
SHA256(stringToConvert[, charSet]) → string
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
stringToConvert |
string | number | date | Yes | The value to hash; a number or date is hashed as the text it renders as |
charSet |
string | No | Name of the character encoding applied before hashing; defaults to UTF-8 |
Example
%%[
VAR @digest
SET @digest = SHA256("Hash probe 2026")
]%%
%%=v(@digest)=%%
Renders eacf88b0a0a8cad1b297c2ea16f0f375482481160be05b72372056503824cf02.
This is the algorithm to pick when a third party specifies a hash, so the value usually goes straight into a link or a request:
%%[
VAR @email, @audienceId
SET @email = "reader@example.org"
SET @audienceId = SHA256(Lowercase(Trim(@email)))
]%%
<img src="https://example.org/px?uid=%%=v(@audienceId)=%%" width="1" height="1">
Normalise the input first, as above — most receiving systems specify lower-cased, trimmed text, and any deviation yields a digest that will never match theirs.
Return value
string — 64 lowercase hexadecimal characters with no separators, measured with Length() on the page rather than assumed.
There is no closed set of sentinel values to test for: every accepted input produces a digest, and every rejected one aborts the page instead of returning an error token.
Behaviour
The digest is the real SHA-256 of the input. Every value here was compared character for character against the same digest computed independently outside Marketing Cloud, over the same bytes. SHA256("Hash probe 2026") gave eacf88b0a0a8cad1b297c2ea16f0f375482481160be05b72372056503824cf02, and the official reference’s own example value reproduced exactly.
The bytes hashed are the UTF-8 encoding of the input. Hashing a string containing ß, € and ä gave eae41ff0f6fc4b2347b9a39213e5149c2730b90296234396f0ae8b961732bfec, the digest of its UTF-8 bytes rather than of the UTF-16 form the engine holds internally. This is the detail that decides whether a digest matches a partner system’s.
The second argument genuinely changes the value. The same input under UTF-16 gave b30cb452193f7d2228a7a89cc5f8f3831fabb63df4e933b1dd985470cf161c89, which is what UTF-16 little-endian bytes produce.
The empty string is hashed, not refused. SHA256("") returned e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, the well-known digest of zero bytes.
The encoding argument is stricter than it looks
ASCII is accepted and silently replaces every character it cannot represent with a question mark before hashing: the non-ASCII string above came back as 0bc518904f83efa53ddaa8caaa94f53429e9913cc9b9248f0658f64a7632234f, the digest of Grus? ??. Nothing warns you.
An unrecognised name is rejected outright — passing banana aborted the page with HTTP 422 rather than falling back to the default.
Show test script
%%[
VAR @b, @a, @nb
SET @b = RequestParameter("b")
SET @a = "Hash probe 2026"
SET @nb = Concat("Grus", Char(223), " ", Char(8364), Char(228))
/* known-good control: renders on every request, so a run of HTTP 422s
can be told apart from a broken deploy */
OutputLine(Concat("CTRL=[", SHA256(@a), "]"))
/* the plain one-argument form, its length, and the empty-input digest */
IF @b == "safe" THEN
OutputLine(Concat("--- safe start ---"))
OutputLine(Concat("S2=[", SHA256(@a), "]"))
OutputLine(Concat("SL=[", Length(SHA256(@a)), "]"))
OutputLine(Concat("ES=[", SHA256(""), "]"))
OutputLine(Concat("--- safe done ---"))
ENDIF
/* the second argument really changes the digest */
IF @b == "enc" THEN
OutputLine(Concat("--- enc start ---"))
OutputLine(Concat("S8=[", SHA256(@a, "UTF-8"), "]"))
OutputLine(Concat("S16=[", SHA256(@a, "UTF-16"), "]"))
OutputLine(Concat("--- enc done ---"))
ENDIF
/* the default encoding is the UTF-8 byte sequence, not the engine's
internal UTF-16 form - only a non-ASCII input can tell them apart */
IF @b == "nonascii" THEN
OutputLine(Concat("--- nonascii start ---"))
OutputLine(Concat("NBIN=[", @nb, "]"))
OutputLine(Concat("NS8=[", SHA256(@nb), "]"))
OutputLine(Concat("NS16=[", SHA256(@nb, "UTF-16"), "]"))
OutputLine(Concat("--- nonascii done ---"))
ENDIF
/* ASCII silently substitutes a question mark for anything it cannot
represent, which changes the digest without any signal */
IF @b == "csascii" THEN
OutputLine(Concat("--- csascii start ---"))
OutputLine(Concat("CSASCII=[", SHA256(@nb, "ASCII"), "]"))
OutputLine(Concat("--- csascii done ---"))
ENDIF
/* each of the three branches below aborts the page - fetch alone */
IF @b == "csbad" THEN
OutputLine(Concat("--- csbad start ---"))
OutputLine(Concat("CSBAD=[", SHA256(@a, "banana"), "]"))
ENDIF
IF @b == "a0" THEN
OutputLine(Concat("--- a0 start ---"))
OutputLine(Concat("A0=[", SHA256(), "]"))
ENDIF
IF @b == "a3" THEN
OutputLine(Concat("--- a3 start ---"))
OutputLine(Concat("A3=[", SHA256(@a, "UTF-8", "extra"), "]"))
ENDIF
]%%
A bare string literal passed to OutputLine renders an empty line while the page still returns HTTP 200, so the marker silently vanishes and the block looks like a function that produced no output. Always wrap it — OutputLine(Concat("--- safe start ---")) — even for a single argument.
When a case involves non-ASCII characters, print the input string alongside the digest. A mangled test string produces a perfectly valid digest of the wrong bytes, which is indistinguishable from a function defect unless the input is visible in the same output.
Availability
| Platform | Available |
|---|---|
| Marketing Cloud Engagement | Yes |
| Marketing Cloud Next | No |
See also
- MD5 · SHA1 · SHA512 — the same signature, different digest lengths
- Official reference · ampscript.guide