Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Campaign URL Builder

marketing.utm_build

ProductionBounded custom (Class B)Deterministicstrictinstantv1.0.0
No network

Overview

You send a link and the campaign parameters you want on it, and we hand back the tagged URL. Only the nine campaign parameter names analytics products read are accepted, so a mistyped name comes back as an error instead of disappearing into your warehouse as a missing channel. Nothing is invented: every parameter emitted is one you sent. If the link already carries one of them you decide what happens — refuse, replace, keep what is there, or add a second occurrence beside it. Path, existing query and fragment are copied character for character, so a link that is already encoded cannot come back double-encoded, and we never fetch, resolve or canonicalise anything.

Campaign links are assembled by hand, in spreadsheets and in templates, and every one of those quietly accepts a mistyped parameter name or an encoder that writes a plus sign where a percent escape belongs. You get one encoding, one order, one closed name list, and an error when something does not fit.

Reliability

Fully deterministic: the same link and parameters give you the same string, on any machine, under any locale or time zone. Failures, and what to do about each: UTM_BUILD_URL_INVALID means the link is not a well-formed absolute URL with a host, or carries a space, a control character or surrounding whitespace, so clean it up and send it again; UTM_BUILD_URL_NOT_ABSOLUTE means there is no scheme, and we resolve nothing against a base, so send the full link; UTM_BUILD_URL_SCHEME_UNSUPPORTED means the scheme is neither http nor https, and a campaign parameter means nothing on a mail or file link; UTM_BUILD_URL_USERINFO_FORBIDDEN means the authority carries credentials, which we refuse rather than copy into a tagged link that ends up in an analytics report; UTM_BUILD_URL_TOO_LARGE means the link you sent is past 8192 code points, so shorten it before tagging; UTM_BUILD_PARAMS_INVALID means campaignParams is empty, and there would be nothing to add, so send at least one entry; UTM_BUILD_PARAM_NAME_UNSUPPORTED means a name is not one of the nine campaign parameters, which is usually a typo worth fixing at the source; UTM_BUILD_PARAM_VALUE_INVALID means a value is not a string or runs past 1024 code points, so send it as a shorter string; UTM_BUILD_PARAM_LIMIT_EXCEEDED means you sent more than 32 entries, which is more campaign parameters than exist, so drop the extras; UTM_BUILD_PARAM_CONFLICT means the link already carries a parameter you sent and the policy is FAIL, so pick REPLACE, KEEP_EXISTING or APPEND and retry; UTM_BUILD_POLICY_UNSUPPORTED means conflictPolicy or emptyValuePolicy is not one of the listed members, and we never quietly fall back to a default; UTM_BUILD_QUERY_LIMIT_EXCEEDED means the link already carries more than 500 query parameters, or the query we would write runs past 8192 characters, so trim the query first; UTM_BUILD_OUTPUT_TOO_LARGE means the tagged link would run past 8192 code points, so shorten the values or the link. None of these is worth retrying unchanged.

Context footprint

Returns the tagged link, a short list per parameter we wrote or skipped, and counters. Your values come back as lengths, not as text. A successful call costs 1 execution credit.

When to use it

When you have a link and the campaign values you want on it, and you want the same bytes out every time.

When you need an arbitrary query parameter, a sorted signing query, or the campaign values read back off an existing link.

Scope

Note
Append caller-supplied campaign parameters to an absolute http or https link, under a published conflict policy, order and encoding.

Non-goals

  • Invents no parameter value — no default source, medium or campaign id
  • Does not sort the query; signing wants sorted keys and that belongs to network.build_query_string
  • Does not canonicalise the link — host case, port, dot segments and existing escapes are left exactly as they arrived
  • Does not fetch, resolve or check the link
  • Does not accept a credential anywhere, including in the authority
  • Does not generate a slug or shorten a link

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

data-transformationencoding

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input64 KB

How we handle your data

Sensitive output possible
What this means

This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response may contain submitted or transformed content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
urlstringYesAbsolute http or https link to tag, up to 8192 code points, with a host and no credentials.
campaignParamsobjectYesClosed map of 1 to 9 campaign parameters from the published utm_* name list, each a string of 0 through 1024 code points.
conflictPolicystringNoWhat to do when the link already carries a campaign parameter you sent; omit it for FAIL.
emptyValuePolicystringNoWhether a blank value is written as name= or skipped; omit it for EMIT.

Output schema

FieldTypeRequiredDescription
urlstringYesTagged link after campaign parameters were encoded and appended, with everything else copied through unchanged.
addedarrayYesOne object per parameter written, with name, position, and valueLength rather than the value itself.
conflictsarrayYesOne object per campaign parameter the link already carried; empty when nothing clashed.
skippedarrayYesEntries not written because KEEP_EXISTING or OMIT applied; empty unless a policy left something out.
encodingobjectYesClosed encoding receipt with space encoding, hex case, and the unreserved character set, so a stored link can be rebuilt byte for byte.
summaryobjectYesClosed counters for query parameter count, how many parameters were added or clashed, reserved characters encoded, and tagged URL length.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"marketing.utm_build","input":{"url":"https://acme.example.com/pricing","campaignParams":{"utm_source":"newsletter","utm_medium":"email"}}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("marketing.utm_build", {"campaignParams":{"utm_source":"newsletter","utm_medium":"email","utm_campaign":"spring sale"},"url":"https://acme.example.com/pricing?plan=team"});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
UTM_BUILD_URL_INVALID400NoThe request payload failed this skill's input validation.
UTM_BUILD_URL_NOT_ABSOLUTE400NoThe request payload failed this skill's input validation.
UTM_BUILD_URL_SCHEME_UNSUPPORTED400NoThe skill does not support the requested variant.
UTM_BUILD_URL_USERINFO_FORBIDDEN422NoThe input was rejected as unsafe.
UTM_BUILD_URL_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.
UTM_BUILD_PARAMS_INVALID400NoThe request payload failed this skill's input validation.
UTM_BUILD_PARAM_NAME_UNSUPPORTED400NoThe skill does not support the requested variant.
UTM_BUILD_PARAM_VALUE_INVALID400NoThe request payload failed this skill's input validation.
UTM_BUILD_PARAM_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
UTM_BUILD_PARAM_CONFLICT412NoThe input is valid but cannot be processed as-is.
UTM_BUILD_POLICY_UNSUPPORTED400NoThe skill does not support the requested variant.
UTM_BUILD_QUERY_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
UTM_BUILD_OUTPUT_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Invents no parameter value — no default source, medium or campaign id
  • Does not sort the query; signing wants sorted keys and that belongs to network.build_query_string
  • Does not canonicalise the link — host case, port, dot segments and existing escapes are left exactly as they arrived
  • Does not fetch, resolve or check the link
  • Does not accept a credential anywhere, including in the authority
  • Does not generate a slug or shorten a link
  • Adding an arbitrary query parameter that is not a campaign parameter
  • Building a signed query where the key order has to be sorted
  • Reading the campaign parameters back off a link

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "marketing.utm_build",
  "input": {
    "campaignParams": {
      "utm_source": "newsletter",
      "utm_medium": "email",
      "utm_campaign": "spring sale"
    },
    "url": "https://acme.example.com/pricing?plan=team"
  }
}

Examples

Tag a pricing page for a newsletter campaign

Inputjson
{
  "campaignParams": {
    "utm_source": "newsletter",
    "utm_medium": "email",
    "utm_campaign": "spring sale"
  },
  "url": "https://acme.example.com/pricing?plan=team"
}
Expected outputjson
{
  "encoding": {
    "spaceEncoding": "percent_20",
    "hexCase": "upper",
    "unreserved": "ALPHA DIGIT - . _ ~"
  },
  "skipped": [],
  "summary": {
    "reservedCharactersEncoded": 1,
    "urlLength": 108,
    "conflictCount": 0,
    "addedCount": 3,
    "queryParameterCount": 4
  },
  "added": [
    {
      "name": "utm_source",
      "position": 1,
      "valueLength": 10
    },
    {
      "name": "utm_medium",
      "position": 2,
      "valueLength": 5
    },
    {
      "name": "utm_campaign",
      "position": 3,
      "valueLength": 11
    }
  ],
  "url": "https://acme.example.com/pricing?plan=team&utm_source=newsletter&utm_medium=email&utm_campaign=spring%20sale",
  "conflicts": []
}
Tip
The existing plan=team parameter keeps its place and its bytes; the campaign parameters follow in the published order.

Replace a campaign source the link already carries

Inputjson
{
  "url": "https://acme.example.com/launch?utm_source=old-list",
  "conflictPolicy": "REPLACE",
  "campaignParams": {
    "utm_source": "spring-list"
  }
}
Expected outputjson
{
  "encoding": {
    "spaceEncoding": "percent_20",
    "hexCase": "upper",
    "unreserved": "ALPHA DIGIT - . _ ~"
  },
  "skipped": [],
  "summary": {
    "reservedCharactersEncoded": 0,
    "urlLength": 54,
    "conflictCount": 1,
    "addedCount": 1,
    "queryParameterCount": 1
  },
  "added": [
    {
      "name": "utm_source",
      "position": 0,
      "valueLength": 11
    }
  ],
  "url": "https://acme.example.com/launch?utm_source=spring-list",
  "conflicts": [
    {
      "name": "utm_source",
      "existingOccurrences": 1,
      "policyApplied": "REPLACE"
    }
  ]
}
Tip
REPLACE drops every existing occurrence and writes yours at the end; the clash is still reported so nothing is lost silently.
  • Tag a landing page for a newsletter or paid campaign with a stable, reproducible encoding
  • Generate hundreds of campaign links from one template without a spreadsheet quietly mistyping utm_source
  • Add a campaign parameter to a link that already carries a query, without touching what is there

Avoid for

  • Adding an arbitrary query parameter that is not a campaign parameter
  • Building a signed query where the key order has to be sorted
  • Reading the campaign parameters back off a link

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

marketingurlcampaignencoding