Skip to main content

Connect AI assistants to OKZest with MCP

OKZest includes an MCP (Model Context Protocol) server so AI assistants and AI-powered tools can work directly with your designs.

Instead of building your own integration, you can connect your OKZest account and ask a tool such as Claude, ChatGPT, or Copilot to:

  • List the designs and projects in your account
  • Check which fields a design supports
  • Generate a live personalised image URL
  • Edit a design's layers and render a one-off image
  • Create and save a new personalised design from a description
  • Find a font to use in a design

What you need​

Before you start, make sure you have:

  1. An OKZest account
  2. At least one design in your account
  3. An API key from Settings -> API Keys
  4. An MCP-compatible AI tool or client

The MCP server uses the same API key as the REST API.

MCP server URL​

Your AI tool should connect to:

https://api.okzest.com/mcp

Example configuration​

Many MCP clients let you define servers in a JSON config file.

Add an OKZest server like this:

{
"servers": {
"okzest": {
"url": "https://api.okzest.com/mcp",
"headers": {
"x-api-key": "YOUR_OKZEST_API_KEY"
}
}
}
}

Replace YOUR_OKZEST_API_KEY with an API key from Settings -> API Keys in your OKZest dashboard.

caution

Keep your API key private. Do not paste it into public repos, public prompts, or client-side code.

Codex Desktop configuration​

Codex Desktop reads MCP server settings from your Codex configuration file.

1. Store your API key in an environment variable​

On Windows, open PowerShell and run the following, replacing the placeholder with your API key:

[Environment]::SetEnvironmentVariable("OKZEST_API_KEY", "YOUR_OKZEST_API_KEY", "User")

2. Add the MCP server to Codex​

Open C:\Users\<your-username>\.codex\config.toml and add:

[mcp_servers.okzest]
url = "https://api.okzest.com/mcp"
env_http_headers = { "x-api-key" = "OKZEST_API_KEY" }

OKZest requires the x-api-key header. Do not use bearer_token_env_var for this connection.

3. Restart Codex​

Restart Codex after saving the file so it can read the environment variable and load the MCP server. Then start a new task and ask, for example:

Using OKZest, list all the projects.

If authentication fails, confirm that the OKZEST_API_KEY environment variable contains a current API key from Settings -> API Keys in OKZest.

How it works​

The MCP server gives your AI tool 15 OKZest actions.

Permissions

The MCP server acts as the user who created the API key, with that user's project permissions, just like the dashboard. The AI tool only sees the projects that user is a member of, and only the designs in projects where they can view designs. Creating, editing and deleting designs need the matching permission in the project. list_projects shows what the user can do in each project.

1. Find your designs​

Technical tool name: list_designs(projectName?, nameContains?, pageSize?, pageNumber?)

This lists every design in your account and returns:

  • Design ID
  • Design name
  • Render URL
  • Project ID and project name
  • Available URL parameters
  • Billing cycle open count
  • Lifetime open count

All parameters are optional:

  • projectName filters the results to designs that belong to a project with that name (case-insensitive, exact match)
  • nameContains filters the results to designs whose name contains the given text (case-insensitive substring match)
  • pageSize and pageNumber page through the results together, for example pageSize: 20, pageNumber: 2 returns the second page of 20 designs

Leave all of them out to list every design in the account.

Use this when you want the AI tool to discover what is available in your account, narrow the search to a specific project or name, or work through a large account a page at a time, for example:

List my OKZest designs in the "Q1 Campaign" project.
Find my OKZest designs with "banner" in the name.

2. List your projects​

Technical tool name: list_projects()

This lists every project in your account and returns:

  • Project ID
  • Project name
  • Design count
  • Billing cycle open count
  • Lifetime open count

Use this when you want the AI tool to discover which projects exist before narrowing a list_designs search, or when you just want an overview of how designs are organised, for example:

What projects do I have in OKZest, and how many designs are in each?

3. Check what a design accepts​

Technical tool name: get_design(id)

This returns the full details for one design, including the available URL parameters for dynamic text and image layers.

Always do this before generating a URL. It tells the AI tool the exact parameter names to use.

For example, a design might support parameters such as:

[
{ "name": "name" },
{ "name": "title" },
{ "name": "company" }
]

4. Generate a live image URL​

Technical tool name: generate_image_url(designId, urlParams?, include_preview?)

This creates a runtime image URL for the design.

urlParams is optional and is passed as a JSON object string, for example:

{"name":"Jane","title":"CEO"}

The returned URL can be dropped straight into an email, web page, workflow, or automation step.

info

OKZest does not pre-render or save the image when this tool runs. It only returns a URL. The image is rendered fresh every time that URL is requested.

Set include_preview to true to also get a small preview of the image, so the AI tool can check the layout. See Previews.

5. Inspect editable layers​

Technical tool name: get_design_fields(designId)

This returns the editable layers for a design, including each layer's layer_id, type, and the exact properties that layer supports.

For example, a text layer may support properties such as text, color, font_size, font_family, font_weight, x, y, width, height, opacity, and stroke, border, or shadow settings. An image layer may support properties such as image_url, x, y, width, height, and opacity.

This mirrors the REST endpoint GET /v1/images/{designId}/fields.

Use this when you want the AI tool to edit real layer properties instead of only filling URL parameters.

6. Modify layers and render an image​

Technical tool name: modify_design_layers(designId, layersJson, backgroundJson?, format?, quality?, include_preview?)

This applies one or more layer edits, renders the image server-side, and returns:

  • imageUrl
  • mimeType

The edits only apply to this render. The saved design does not change, so to keep a change, make it in the OKZest editor. This tool edits a design's existing layers and cannot add new ones; use create_design for that.

format is jpeg, png or webp. Leave it out to get a JPEG, or a PNG if the design has a transparent background (no background colour and no background image). quality (1-100) applies to JPEG and WebP only. Set include_preview to true to also get a small preview of the render; see Previews.

layersJson is passed as a JSON array string, for example:

[
{
"layer_id": "heading",
"text": "Hello",
"color": "#ff0000"
}
]
caution

modify_design_layers returns a temporary URL

generate_image_url and modify_design_layers are not interchangeable.

  • generate_image_url returns a permanent runtime URL. Nothing is stored, so you can reuse that URL indefinitely.
  • modify_design_layers renders once, stores the result temporarily, and returns a temporary URL. The image is deleted after about a day.

Download, forward, or display the modify_design_layers result straight away. Do not save it as a permanent embed URL.

If you change font_family, use a value returned by list_fonts.

7. Find a font​

Technical tool name: list_fonts(search?, projectId?)

This lists the fonts a design can use and returns, for each font:

  • font_family - the exact value to pass as font_family in create_design or modify_design_layers
  • label - the name shown in the editor's font picker
  • source - custom for fonts you have uploaded, ms for Microsoft fonts, or google for Google Fonts

Uploaded fonts come first, then the Microsoft fonts, then the Google fonts. For an uploaded font, font_family is different from label, so always pass font_family.

Uploaded fonts are switched on per project in Custom fonts: a font is listed if it is on for that project or for every project. Pass projectId (from list_projects) to include that project's uploaded fonts. You can leave it out if your account has only one project; with more than one, leaving it out lists no uploaded fonts. A design can only use the uploaded fonts that are on for its own project.

There are about 1,750 fonts, so the tool returns at most 100. The response also includes total_count. If total_count is higher than the number of fonts returned, ask for a narrower search.

search is optional and matches the font name, ignoring case, for example:

list_fonts(search: "roboto", projectId: "YOUR_PROJECT_ID")

8. Create a new design​

Technical tool name: create_design(name, width, height, layersJson, backgroundJson?, projectId?)

This creates and saves a new design in your account, the same as one made in the editor. It returns:

  • design - the saved design, including its design ID, render URL and URL parameters
  • previewImageUrl - a temporary preview of the design with its default values
  • previewError - why the preview is missing, if it could not be rendered. The design is still saved.

The inputs are:

  • name - the design name
  • width and height - the canvas size in pixels, from 1 to 3508
  • layersJson - a JSON array of layers, drawn in order, so later layers sit on top
  • backgroundJson - optional background colour, for example {"color":"#ffffff"}. The default is white.
  • projectId - the project to save the design in. This is required if your account has more than one project, so ask the AI tool to call list_projects first, or create_project to save it in a new project.

Each layer needs a layer_id (1-50 letters, numbers, - or _) and a type of text or image. It can also set any property modify_design_layers accepts, such as x, y, width, height, color, font_size, font_family, font_weight, text_align and opacity.

How personalisation works in a new design:

  • Dynamic text: put the layer's own ID in square brackets in its text, for example "text": "Hi [first_name]!" on a layer with "layer_id": "first_name". The placeholder is replaced by the URL parameter of the same name, and default_text is shown when no value is given. Leave text out to get just the placeholder.
  • Static text: text without the placeholder never changes.
  • Images: an image layer needs an image_url, which is the default image. The URL parameter named after the layer swaps the image on each render. The image is resized to the layer's width and keeps its aspect ratio.
  • Background photo: add an image layer at x 0, y 0, sized to the canvas, as the first layer. backgroundJson only takes a colour.
  • Fonts: font_family must be a value returned by list_fonts. Text layers use Open Sans by default. font_weight takes bold, normal or a number from 100 to 900; 600 and above draws the font's bold file. font_style takes italic. A font without a bold or italic file, such as Anton, draws its regular file. The same applies when modify_design_layers changes a layer's weight or style.

For example:

[
{
"layer_id": "first_name",
"type": "text",
"text": "Hi [first_name]!",
"default_text": "there",
"x": 40,
"y": 40,
"width": 520,
"font_size": 48,
"color": "#111111"
},
{
"layer_id": "photo",
"type": "image",
"image_url": "https://example.com/photo.jpg",
"x": 40,
"y": 160,
"width": 200,
"height": 200
}
]

This design gets two URL parameters, first_name and photo, so it is ready to use with generate_image_url straight away. You can open it in the OKZest editor to fine-tune it.

Unlike modify_design_layers, create_design saves: the design stays in your account until you delete it. Use generate_image_url on the new design for its permanent image URL; the preview URL expires.

info

create_design needs an API key that is linked to a user. If you see "This API key is not linked to a user", create a new API key in Settings -> API Keys and use that.

9. Create a project​

Technical tool name: create_project(name)

This creates a new, empty project, the same as New project in the dashboard, and returns its project ID and name. Pass the project ID to create_design to save designs in it. For example:

Create an OKZest project called "Acme Spring Campaign" and make a 600x300 welcome banner in it.

Project names do not have to be unique, so the AI tool should call list_projects first and reuse a project that already exists.

The project is created as the user who created the API key, and that user is added to the project with full access. The user needs permission to create projects, the same permission that shows New project in the dashboard.

info

create_project needs an API key that is linked to a user. If you see "This API key is not linked to a user", create a new API key in Settings -> API Keys and use that.

10. Save changes to a design​

Technical tool name: update_design_layers(designId, layersJson, backgroundJson?)

This saves layer edits to a design, for example to fix a typo or change a colour. It takes the same layersJson as modify_design_layers, but modify_design_layers only renders a one-off image, while this tool changes the saved design.

warning

OKZest renders an image each time it is opened, so the change also shows in emails and pages you have already sent with this design.

  • Text containing the layer's own ID in square brackets, for example [first_name], stays personalised. Text without it is static.
  • A new image_url keeps the layer's width, and its height follows the image's aspect ratio.
  • backgroundJson takes a colour only. Change a background image in the editor.

It returns the saved design and a temporary preview. It needs permission to edit designs in the design's project.

11. Copy a design​

Technical tool name: duplicate_design(designId, name, projectId?)

This copies a design as a new design, with its own design ID, render URL and copies of its uploaded images. It is saved in the same project unless you give a projectId. This is handy for starting a new client's version of a template, for example with create_project first. It needs permission to create designs in the project the copy goes to.

12. Move a design​

Technical tool name: move_design(designId, projectId)

This moves a design to another project. The design ID and render URL stay the same, so images already sent keep working. It needs permission to edit designs where the design is, and to create designs where it goes.

13. Rename a design or project​

Technical tool names: rename_design(designId, name) and rename_project(projectId, name)

Renaming needs permission to edit designs in the project, or to edit the project. Render URLs do not change.

14. Delete a design​

Technical tool name: delete_design(designId)

This deletes a design that has never been opened, such as a draft or a mistake the AI tool just made. A design that has been opened may be in emails or pages already sent, and deleting it would break those images, so the MCP server refuses. Delete those in the dashboard if you are sure. Previews in the dashboard and from the MCP server do not count as opens. It needs permission to delete designs in the design's project. Deleting cannot be undone.

15. Delete a project​

Technical tool name: delete_project(projectId)

This deletes a project and every design in it, but only when none of those designs has ever been opened, such as an empty project or a draft project the AI tool just made. If even one design has been opened, the MCP server refuses and deletes nothing, because opened designs may be in emails or pages already sent. Delete those projects in the dashboard if you are sure.

It needs permission to delete projects (the same permission that shows Delete project in the dashboard) and permission to delete designs in that project. Other team members lose access to the project too. Deleting cannot be undone.

Previews​

generate_image_url and modify_design_layers take an optional include_preview. Set it to true and the result also includes a small JPEG of the image (at most 512 pixels wide and 1024 tall, never enlarged, transparency shown on white), so the AI tool can see the layout without opening the URL.

  • A preview counts as one render. For modify_design_layers it is the same render, so the call still counts once. For generate_image_url, which normally renders nothing, the preview renders the image once.
  • If the account has reached its plan limit, the preview shows the design's default text.
  • PDF output has no preview, and a preview that fails or times out comes back as a short note instead. The URL is still returned.

Limits and tool hints​

  • Each account can make up to 120 MCP requests a minute. Over that, the server answers with HTTP 429.
  • Up to 20 render calls a minute: create_design, modify_design_layers, and generate_image_url with include_preview. Over that, the tool returns an error asking the AI tool to wait a minute and try again.

If you hit a limit, see Troubleshooting and FAQ.

Each tool also tells your AI tool what it does, so the tool can decide which calls to approve automatically. list_projects, list_designs, get_design, get_design_fields, list_fonts and generate_image_url only read. create_project, create_design and duplicate_design add a project or design, and modify_design_layers stores a temporary image. update_design_layers, move_design, rename_design and rename_project change saved designs or projects, and delete_design and delete_project delete a design or project that has never been opened.

Error messages​

When a request is wrong, the MCP server tells your AI tool what to fix, so it can correct the call and try again. For example:

  • An unknown design or project ID
  • JSON in layersJson, backgroundJson or urlParams that cannot be read
  • A font_family that does not exist. The message suggests the right name when it is only a case or label mix-up, and says to call list_fonts.
  • A canvas size outside 1-3508 pixels
  • A user without permission for the action, for example to create projects or to edit designs in a project
  • A design that cannot be deleted because it has been opened
  • Too many render calls in a minute

For best results, ask your AI tool to follow one of these orders:

For discovering what you have​

  1. list_projects
  2. list_designs (optionally filtered by projectName or nameContains)

For URL-parameter personalisation​

  1. list_designs
  2. get_design
  3. generate_image_url

For layer editing​

  1. list_designs
  2. get_design_fields
  3. list_fonts (only if you are changing the font)
  4. modify_design_layers

For creating a new design​

  1. list_projects (if your account has more than one project)
  2. create_project (only if the design belongs in a new project)
  3. list_fonts with that projectId (if you want a font other than Open Sans)
  4. create_design
  5. generate_image_url with the new design ID

This helps the AI tool choose the right design and use valid parameter names or layer properties.

Worked example​

Here is a simple example using an AI assistant to create a personalised image URL.

Step 1 - ask the assistant to find your design​

List my OKZest designs and find the one for my welcome email banner.

The assistant should use list_designs and return the matching design. If you know which project it lives in, you can narrow the search with list_designs(projectName: "Marketing").

Step 2 - ask it to inspect the design​

Get the details for that design and show me the available parameters.

The assistant should use get_design(id) and identify the parameter names, such as name and title.

Step 3 - ask it to create the URL​

Generate a personalised image URL for Jane, title CEO.

The assistant should use:

generate_image_url(
designId: "YOUR_DESIGN_ID",
urlParams: "{\"name\":\"Jane\",\"title\":\"CEO\"}"
)

The result will be a live OKZest image URL that you can paste into an <img> tag, an email platform, or an automation tool.

Worked example: editing design layers​

Here is a simple example using an AI assistant to edit a layer's text and colour before rendering the image.

Step 1 - ask the assistant to find your design​

List my OKZest designs and find the one for my webinar banner.

The assistant should use list_designs and return the matching design.

Step 2 - ask it to inspect the editable layers​

Show me the editable layers for that design and tell me which properties the heading layer supports.

The assistant should use get_design_fields(designId) and identify a layer such as heading with editable properties including text and color.

Step 3 - ask it to render the edited image​

Change the heading text to Hello Jane and make it red.

The assistant should use:

modify_design_layers(
designId: "YOUR_DESIGN_ID",
layersJson: "[{\"layer_id\":\"heading\",\"text\":\"Hello Jane\",\"color\":\"#ff0000\"}]"
)

The result will be a temporary image URL. Use it straight away, before it is deleted. Add include_preview: true if you want the assistant to see the image and check it.

Worked example: creating a design​

Here is a simple example using an AI assistant to build a new personalised design from scratch.

Step 1 - describe the design​

Create an OKZest design called "Welcome banner", 600 by 300 pixels with a white background.
Add the text "Hi [first_name]!" in Montserrat, size 48, with "there" as the default,
and a profile photo layer on the right.

The assistant should call list_fonts(search: "montserrat") to get the exact font_family, then call create_design with the layers.

Step 2 - check the preview​

The assistant gets back the saved design and a previewImageUrl. Ask it to show you the preview. If something looks wrong, ask for changes, or open the design in the OKZest editor.

Step 3 - use it​

Generate a personalised image URL for Jane using that design.

The assistant should use generate_image_url with the new design ID and {"first_name":"Jane"}.

Good use cases​

The MCP server is a good fit when you want an AI tool to help with:

  • Email personalisation
  • Sales outreach images
  • Customer onboarding images
  • Zapier or Make workflows
  • A/B testing different personalised image variants
  • Editing a design's layers programmatically via AI
  • Creating new designs from a description, without opening the editor
  • Auditing or organising a large design library by project, with search and pagination

If you are already using automation tools, see the Zapier integration page as well.

Authentication​

OKZest authenticates MCP requests with the x-api-key HTTP header.

  • Missing API key: the request is rejected
  • Invalid API key: the request is rejected
  • Valid API key: the AI tool acts as the user who created the key, with that user's project permissions

This is the same API key model used by the REST API.

When to use MCP vs REST API​

Use MCP when you want an AI assistant or AI-powered tool to discover designs, create new designs, generate runtime image URLs, or render one-off layer-edited images for you. Creating designs and listing fonts are only available through MCP.

Use the REST API when you want to build a traditional coded integration or request binary image output directly.

Skill file for your AI tool​

If your AI tool supports installable "skills" (for example, Copilot CLI, Claude, or other agent skill directories), you can download a ready-made skill file that teaches it the full OKZest MCP workflow:

Download okzest-image-generation SKILL.md

Save it into your tool's skills folder (e.g. .github/skills/okzest-image-generation/SKILL.md, .claude/skills/, or .agents/skills/) alongside the MCP server configuration above.