On this page
- Style 1: configuring Photopea through the URL hash
- The save protocol: what your server actually receives
- The CORS requirement that stops most first attempts
- Style 2: live messaging between your page and the editor
- What live messaging is good for
- Style 3: plugins as panels inside the editor
- Cost, stability and the whitelabel question
- Is the API a reason to choose Photopea?
- Sources for this chapter
Reference · Developer documentation
Photopea is unusual among browser editors in that it exposes a documented integration surface. You can embed the whole editor in an iframe, preload files into it, drive it with scripts, receive the edited result back as binary data, and add your own panels to its interface. This chapter documents that surface from the official specification, so that a developer can evaluate it without reading the source. It is part of the wider Photopea reference, and every claim here is traceable to the pages listed at the end.
There are three integration styles, and they solve different problems. The hash configuration is a one-shot setup: you build a URL that tells Photopea which files to open and where to save. Live messaging is a two-way channel: your page and the editor exchange scripts and binary files through postMessage. Plugins are panels you add to the editor's own interface, which are really just your website running in a frame with live messaging available to it.
Style 1: configuring Photopea through the URL hash
Photopea accepts a JSON configuration object after a hash sign. The string must be URL-encoded, equivalent to JavaScript's encodeURIComponent(), and the resulting URL can either be opened directly or used as the src of an iframe.
https://www.photopea.com#{ "files": [ ... ], "server": { ... } }
Every field is optional. The object accepts the following parameters.
| Parameter | Type | What it does |
|---|---|---|
files | array of strings | Files loaded when Photopea starts. Each entry is a URL or a data: URI, so a file can be passed inside the request itself. |
resources | array of strings | Resources loaded at start: brushes (.ABR), gradients (.GRD), fonts (.otf) and similar. |
server | object | Where documents are saved. Adds a working File → Save to every open document. |
server.version | integer | Version of the save protocol. Currently 1. |
server.url | string | Your endpoint. Photopea POSTs the document there when the user saves. |
server.formats | array of strings | Formats to send, in the same notation as saveToOE, for example "psd:true", "png", "jpg:0.5". |
environment | object | Interface and behaviour settings, including customIO scripts for the Open and Save buttons. |
apis | object | Third-party keys. dezgo unlocks Magic Replace and Remove BG through Dezgo; dezgoURL redirects those requests to your own server. |
script | string | A script executed after each file loads. Can be long, since it is part of the encoded object. |
The save protocol: what your server actually receives
When server is configured, every document gains a File → Save action. Pressing it sends a single POST request to your endpoint containing binary data in two parts: the first 2,000 bytes are JSON, and everything after them is one or more encoded image files.
{
"source" : "https://www.mysite.com/images/button.png",
"versions" : [
{ "format": "psd", "start": 0, "size": 700000 },
{ "format": "jpg", "start": 700000, "size": 100000 }
]
}
source identifies the document. If it was loaded from your server, it is that URL; if it was opened locally or created inside Photopea, it takes the form "local,X,NAME" where X is the document's integer id. Each entry in versions gives a format and a byte range, with start measured from the end of the 2,000-byte JSON block. That layout means a server can write each version to disk by seeking rather than buffering the whole request, which is why the specification shows a short PHP reader that reads exactly 2,000 bytes, parses the JSON, then streams the remainder.
Your endpoint can reply with a JSON object containing any of three optional fields. message displays a short confirmation to the user. script is executed in the editor, which is how you would call app.echoToOE("saved") to notify the outer page. newSource replaces the document's source value, which matters for files created inside Photopea, where the original source is a local,… placeholder that needs to become a real identifier for the next save.
The CORS requirement that stops most first attempts
Browsers only let a page read files from its own origin unless the server explicitly permits otherwise. For Photopea to load a file from your domain, your server's response must include the header Access-Control-Allow-Origin: *. Without it, the request is blocked before Photopea sees any data, and the failure appears in the browser console as a CORS error rather than as an editor error. This single header is the most common reason a first integration does not work.
Style 2: live messaging between your page and the editor
When Photopea is embedded in a frame, the hosting page — the specification calls it the Outer Environment, or OE — can talk to it with standard web messaging. The OE sends either a string, which is executed as a script, or an ArrayBuffer, which is treated as a binary file: a PSD, SVG, JPEG, font or brush.
window.addEventListener("message", function (e) { /* e.data */ });
var wnd = document.getElementById("pp").contentWindow;
wnd.postMessage(msg, "*");
The handshake is simple and worth respecting. When Photopea has initialised and can accept commands it sends the string "done". After each message of yours has been processed it sends "done" again. A robust integration waits for the first "done" before sending anything, and queues work until the next one arrives, rather than firing a burst of messages at a frame that is still starting up.
Getting data back out uses two script calls. app.activeDocument.saveToOE("gif") sends the current document to the OE as an ArrayBuffer in the requested format, followed by a "done" string once the script finishes. app.echoToOE("Hello") sends an arbitrary string, which is how a script reports status back to your page.
What live messaging is good for
The specification lists four patterns, and they are the ones that come up in practice. You can use Photopea as a hidden module, driving it entirely by script with its interface out of the way, which turns it into an image-processing engine. You can build a batch processor that resizes, watermarks or converts a folder of images. You can export every layer of a document as a separate PNG. And you can build a template generator that replaces the text in each text layer with data from your own system, which is how business-card and certificate generators are usually built.
The most complete pattern is a custom storage backend. You redefine what Open and Save mean using the customIO environment scripts, so that pressing Open notifies your page instead of showing a file dialogue, your page supplies the file as an ArrayBuffer, and you set app.activeDocument.source to your own identifier. On save, you read the document with saveToOE and read its source with echoToOE(app.activeDocument.source), then write both into your storage. The result is an editor that reads and writes only to your system, with no server round-trip that Photopea controls.
Style 3: plugins as panels inside the editor
A plugin is a website shown in a panel on the right-hand side of Photopea. You declare it in the configuration JSON under environment.plugins, giving a name, a url and an optional icon. Width and height are accepted but the specification recommends leaving them out so the panel adapts to any size.
{ "environment": { "plugins": [
{ "name": "Wikipedia", "url": "https://en.wikipedia.org",
"icon": "https://en.wikipedia.org/static/favicon/wikipedia.ico" }
] } }
Because the plugin is your page in a frame, it has live messaging available to it: it can call window.parent.postMessage(script, "*") to run a script in the editor, or post an ArrayBuffer to hand over an image or a resource. That is enough to build a photo store that opens a chosen image, a font gallery that loads a chosen font, or a paid panel that handles its own sign-in and billing inside the same frame. Users can also drag images from your plugin page onto the canvas, because Photopea accepts drops from any browser-opened page.
Two icon conventions are worth following. Make the icon background transparent and the artwork black, and prefix the URL with === so Photopea recolours it to match the current theme. Size the artwork as a multiple of 20×20 screen pixels — a 160×160 icon with 8-pixel stripes stays sharp, while 9-pixel stripes will blur.
Cost, stability and the whitelabel question
The API is free to use, including the live-messaging and plugin paths. The specification is candid that the API is in an early stage and that there may be critical bugs, and it disclaims responsibility for documents edited or generated through it — a fair warning to design your integration so that a failed script cannot silently corrupt a user's file. Advertising and the editor's coloured buttons can be removed through a Distributor account, which is the whitelabel route; that is a commercial arrangement rather than a technical one.
Is the API a reason to choose Photopea?
For a developer deciding between browser editors, the API is a genuine differentiator. Very few free browser editors expose a documented, scriptable embedding surface with a defined save protocol and a plugin mechanism. If you are building a product that needs image editing as a component — a print-on-demand store, a template generator, a batch pipeline — the ability to drive an editor by script and receive the result as bytes is worth more than any individual filter. If you are simply editing your own photos, none of this matters and the limitations chapter is the more relevant reading.
Sources for this chapter
This page is a summary of four official documents, and every technical claim above comes from them rather than from testing. The configuration object, the save protocol and the CORS requirement are from the API specification at photopea.com/api. The messaging handshake, saveToOE and echoToOE are from the Live Messaging page at photopea.com/api/live. The plugin structure and icon conventions are from photopea.com/api/plugins. The customIO environment scripts are described at photopea.com/api/environment. Where the specification is silent — for example on rate limits or on the maximum size of a message — this chapter does not guess. See the sources chapter for the evidence tiers used across the reference and the editorial policy for how corrections are handled.