Transformation Serializer

The bidirectional translator between Cloudinary transformation URL strings and the structured transformation model — the JSON/TypeScript object form of the same transformation.

"c_scale,w_200/e_blur:500"   ⇄   [{ actionType: 'scale', dimensions: { width: 200 } },
                                  { actionType: 'blur', strength: 500 }]

Reach for it whenever a transformation has to be edited rather than merely rendered: a UI that lets someone drag a crop handle needs the model, and the delivery URL needs the string.

What this service exposes#

Surface Endpoint Use it when
HTTP API POST /api/v0/encode, POST /api/v0/decode You are on a server, or in a language other than JavaScript.
ESM bundle GET /esm/latest/serializer.mjs You are in a browser and want zero round-trips per keystroke.
JSON Schema GET /schema/v0/transformation.json You want to validate or generate types for a model.
Specification GET /docs/latest/specification You want the language itself: its grammar, its canonical form, and what the encoder and decoder guarantee.

Try it in 30 seconds#

Decode a string into a model:

curl -s https://production-transformation-serializer-us-east-1.cloudinary.com/api/v0/decode \
  -H 'content-type: application/json' \
  -d '{"transformationString":"c_scale,w_200/e_blur:500"}'
{
  "model": [
    { "actionType": "scale", "dimensions": { "width": 200 } },
    { "actionType": "blur", "strength": 500 }
  ]
}

…and encode it back:

curl -s https://production-transformation-serializer-us-east-1.cloudinary.com/api/v0/encode \
  -H 'content-type: application/json' \
  -d '{"model":[{"actionType":"scale","dimensions":{"width":200}},{"actionType":"blur","strength":500}]}'
{ "transformationString": "c_scale,w_200/e_blur:500" }

Or do the same in the browser, with no network round-trip at all:

<script type="module">
  import { encode, decode } from 'https://production-transformation-serializer-us-east-1.cloudinary.com/esm/latest/serializer.mjs';

  decode('c_scale,w_200/e_blur:500');
</script>

The playground runs exactly that bundle in your browser — nothing you type there is sent anywhere.

The console on this page already has it#

Every page of these docs loads the published bundle and puts encode and decode on window. Open devtools, right here, and call them:

decode('c_scale,w_200/e_blur:500');
encode([{ actionType: 'scale', dimensions: { width: 200 } }]);

It is the real bundle, pinned to the version this page documents — not a sandbox, and not a reimplementation that could disagree with what ships. serializer is there too, if you prefer the { encode, decode } pair.

Two things that surprise people#

Encoding normalises. Qualifiers come out in alphabetic order within an action, so { width: 500, height: 300 } encodes to c_scale,h_300,w_500, not c_scale,w_500,h_300. A decode → encode round-trip is therefore stable but not byte-identical to the input string.

Unknown syntax survives. Anything the decoder does not recognise becomes a custom action rather than being dropped, so decoding and re-encoding an exotic transformation gives you back what you started with. You do not have to wait for the model to learn a qualifier before you can safely pass it through.

Versioning#

v0 in /api/v0 and /schema/v0 names the contract these pages describe. A change that would break a caller — a renamed endpoint, a model that means something different — ships under v1 beside it, so a URL you integrate against today keeps working as long as v0 is served.

The browser bundle needs no contract version: /esm/v0.1.0/serializer.mjs carries its release and is frozen for good. Nor do these pages: /docs/latest describes the release named in the sidebar.

Going deeper#

The specification defines the transformation language itself: its grammar, the schema, expressions and conditions, the canonical form, and what the encoder and decoder guarantee.