CloudPagesURL
Builds the published URL of a CloudPages landing page. Runtime-proven on a live Marketing Cloud Engagement CloudPage — extra arguments must come in pairs, and a page ID that matches no page aborts the request instead of returning an empty string.
Syntax
CloudPagesURL(pageId[, paramName1, paramValue1, paramNameN, paramValueN, ...]) → string
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
pageId |
string | number | Yes | CloudPages page ID (number or string) |
paramName1 |
string | No | Query parameter name |
paramValue1 |
string | No | Query parameter value |
paramNameN |
string | No | Additional query parameter name |
paramValueN |
string | No | Additional query parameter value |
Name and value are supplied as a pair, and the pairs repeat for as long as you need them. There is no upper bound on the argument count.
Example
%%[
VAR @link
SET @link = CloudPagesURL(39412)
]%%
<a href="%%=v(@link)=%%">Open the page</a>
Renders the landing page’s own published URL, unchanged and without a query string — the same address you would copy out of the page’s properties.
Add a pair when the target page needs a value, and read it back there with RequestParameter:
%%[
VAR @link
SET @link = CloudPagesURL(39412, "offer", "spring")
]%%
<a href="%%=v(@link)=%%">See your offer</a>
The rendered href now carries one extra query parameter holding a single encrypted token. Neither offer nor spring is readable in it — see below.
Return value
string — the published URL of the referenced page.
The value domain is open, so there is no set of sentinel values to test for. What is fixed is the shape: with no extra pairs it is the bare page URL; with pairs it is that same URL plus exactly one query parameter, whatever number of pairs you passed.
Behaviour
A plain call returns the page’s direct URL, not a wrapper. Called with a page ID from an ordinary anonymous page request, the result was byte-for-byte the referenced page’s own published address — same host, same path, no query string, and no tracking or redirect host in front of it. Nothing needs unwrapping before you can use it.
Extra arguments must come in pairs. One, three and five arguments all render. Two and four abort the request with HTTP 422 and discard everything already written, so a name left without its value takes the whole page down rather than being ignored.
A page ID that matches no page aborts the request. It does not return an empty string and it does not return an error value — the request fails outright with HTTP 422, discarding output already written. This is the single most important thing to know about the function: a mistyped ID looks exactly like a broken page, and gives you nothing to test for. Validate the ID before you build the link — see the card on this.
Pairs are encrypted, not appended. A pair produced one extra query parameter carrying a long opaque token in which neither the name nor the value could be found. A second pair only lengthened that same token. Do not expect to read your own parameters back out of the URL string.
Encoding a value is therefore moot. A value containing a space and an ampersand produced a result with no space and no ampersand anywhere in it. The characters that would need escaping never reach the URL as text in the first place.
The token changes on every request. The same call fetched twice returned two different tokens of equal length, so the result must never be compared, deduplicated or cached as an identity. Two calls inside a single render do match — a comparison that passes in one page is not evidence the value is stable.
The ID may be a number or a quoted string. Both spellings of the same ID produced the identical URL.
Show test script
%%[
VAR @b, @pid, @known, @u1, @u2
SET @b = RequestParameter("b")
/* the page this script is deployed to, and that page's own URL */
SET @pid = 39412
SET @known = "your-subdomain.pub.sfmc-content.com/your-path"
/* known-good control: renders on every request, so a gate that aborts
can be told apart from a broken deploy */
OutputLine(Concat("CTRL=[", Uppercase("ok"), "]"))
OutputLine(Concat("GATE=[", @b, "]"))
/* one argument: the returned string IS the page's own published URL,
so the known URL is found at position 9, right after https:// */
IF @b == "ar1" THEN
OutputLine(Concat("--- ar1 start ---"))
SET @u1 = CloudPagesURL(@pid)
OutputLine(Concat("R=[", @u1, "]"))
OutputLine(Concat("LEN=[", Length(Concat(@u1, "")), "]"))
OutputLine(Concat("HOSTPOS=[", IndexOf(@u1, @known), "]"))
OutputLine(Concat("QSPOS=[", IndexOf(@u1, "?"), "]"))
OutputLine(Concat("--- ar1 done ---"))
ENDIF
/* one pair: a single query parameter appears, and neither the name nor
the value can be found in the result */
IF @b == "ar3" THEN
OutputLine(Concat("--- ar3 start ---"))
SET @u1 = CloudPagesURL(@pid, "k1", "v1")
OutputLine(Concat("R=[", @u1, "]"))
OutputLine(Concat("LEN=[", Length(Concat(@u1, "")), "]"))
OutputLine(Concat("KPOS=[", IndexOf(@u1, "k1"), "]"))
OutputLine(Concat("VPOS=[", IndexOf(@u1, "v1"), "]"))
OutputLine(Concat("--- ar3 done ---"))
ENDIF
/* two pairs: still one query parameter, just a longer token */
IF @b == "ar5" THEN
OutputLine(Concat("--- ar5 start ---"))
SET @u1 = CloudPagesURL(@pid, "k1", "v1", "k2", "v2")
OutputLine(Concat("R=[", @u1, "]"))
OutputLine(Concat("LEN=[", Length(Concat(@u1, "")), "]"))
OutputLine(Concat("--- ar5 done ---"))
ENDIF
/* the ID as a quoted string behaves like the numeric literal */
IF @b == "str" THEN
OutputLine(Concat("--- str start ---"))
SET @u1 = CloudPagesURL("39412")
OutputLine(Concat("R=[", @u1, "]"))
OutputLine(Concat("LEN=[", Length(Concat(@u1, "")), "]"))
OutputLine(Concat("--- str done ---"))
ENDIF
/* a value holding a space and an ampersand: neither character appears
in the result, because the pair never survives as text */
IF @b == "enc" THEN
OutputLine(Concat("--- enc start ---"))
SET @u1 = CloudPagesURL(@pid, "q", "a b&c")
OutputLine(Concat("R=[", @u1, "]"))
OutputLine(Concat("SPACEPOS=[", IndexOf(@u1, " "), "]"))
OutputLine(Concat("AMPPOS=[", IndexOf(@u1, "&"), "]"))
OutputLine(Concat("--- enc done ---"))
ENDIF
/* two identical calls inside ONE render match; fetch this gate twice and
compare the ar3 tokens across the two requests to see them differ */
IF @b == "det" THEN
OutputLine(Concat("--- det start ---"))
SET @u1 = CloudPagesURL(@pid)
SET @u2 = CloudPagesURL(@pid)
OutputLine(Concat("SAME=[", IIf(@u1 == @u2, "identical", "different"), "]"))
OutputLine(Concat("--- det done ---"))
ENDIF
/* argument counts the signature does not allow, and an ID matching no
page: each aborts its own branch with HTTP 422 while every branch
above still renders */
IF @b == "ar0" THEN
OutputLine(Concat("AR0_START=[x]"))
OutputLine(Concat("R=[", CloudPagesURL(), "]"))
ENDIF
IF @b == "ar2" THEN
OutputLine(Concat("AR2_START=[x]"))
OutputLine(Concat("R=[", CloudPagesURL(@pid, "k1"), "]"))
ENDIF
IF @b == "ar4" THEN
OutputLine(Concat("AR4_START=[x]"))
OutputLine(Concat("R=[", CloudPagesURL(@pid, "k1", "v1", "k2"), "]"))
ENDIF
IF @b == "bad" THEN
OutputLine(Concat("BAD_START=[x]"))
OutputLine(Concat("R=[", CloudPagesURL(987654321), "]"))
ENDIF
]%%
Two things this harness cannot reach. What the token resolves to on the target page needs a real send with a real subscriber, so the personalisation the official reference describes is untested here — only the token’s existence, its per-call variation and its absorption of the extra pairs were proven. And the reserved parameter names the official reference lists were not exercised at all, so that list stands unchallenged and unconfirmed.
Availability
| Platform | Available |
|---|---|
| Marketing Cloud Engagement | Yes |
| Marketing Cloud Next | No |
See also
- MicrositeURL — the Classic Content microsite counterpart, which accepts an ID that matches nothing instead of aborting
- RedirectTo — wrap the result when link tags would otherwise break the URL
- Unknown page IDs abort the request — the finding in full
- Official reference · ampscript.guide