---
title: "Phoenix"
description: "Generate dynamic OG images and on-demand graphics in Phoenix with the Imejis.io API. A secure controller proxy that keeps your API key server-side and returns a cached image."
url: "https://www.imejis.io/apis/phoenix"
published: "2026-07-27"
---

# Phoenix

## Phoenix

Generate a unique OG image for every page, or any on-demand graphic, from a single Imejis template. The pattern below uses a Phoenix controller action as a thin proxy: your API key stays on the server, and the browser (and social crawlers) only ever see your own URL.

Set two environment variables first: `IMEJIS_API_KEY` (from your dashboard) and `IMEJIS_DESIGN_ID` (any design's ID).

```bash title=".env / runtime environment"
IMEJIS_API_KEY=your_api_key
IMEJIS_DESIGN_ID=your_design_id
```

### 1. A controller that renders the image

This action takes a `title` query param, POSTs it to Imejis as a `component.property` override with the `dma-api-key` header, and returns the finished image bytes. Because the key is sent server-side, it never reaches the client.

```elixir showLineNumbers title="lib/my_app_web/controllers/og_controller.ex"
defmodule MyAppWeb.OgController do
  use MyAppWeb, :controller

  def show(conn, params) do
    title = Map.get(params, "title", "Untitled")
    design_id = System.get_env("IMEJIS_DESIGN_ID")
    api_key = System.get_env("IMEJIS_API_KEY")

    %Req.Response{body: image} =
      Req.post!("https://render.imejis.io/v1/#{design_id}",
        headers: [{"dma-api-key", api_key}],
        json: %{"headline.text" => title}
      )

    conn
    |> put_resp_content_type("image/jpeg")
    # Renders are deterministic, so cache aggressively at the edge/CDN.
    |> put_resp_header(
      "cache-control",
      "public, immutable, no-transform, max-age=31536000"
    )
    |> send_resp(200, image)
  end
end
```

Wire the action into your router:

```elixir showLineNumbers title="lib/my_app_web/router.ex"
scope "/", MyAppWeb do
  pipe_through :browser
  get "/og", OgController, :show
end
```

### 2. Point your template at it

Reference the route from your HEEx template or meta tags, passing each page's real data. Social platforms fetch `/og?title=…` from your own domain, no key exposed.

```html showLineNumbers title="lib/my_app_web/components/layouts/root.html.heex"
<% og_image = "https://your-site.com/og?title=" <>
URI.encode_www_form(@post.title) %>
<meta property="og:image" content="{og_image}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="{og_image}" />
```

**Override any field.** Each dynamic component exposes keys like `headline.text`, `title.color`, or `image.image`. Send only the ones you want to change. A design's overridable fields are listed in its Share / API panel.
