Webhooks

Know the moment an image lands in storage

Subscribe an HTTPS endpoint to the events you care about. HTML/CSS to Image signs every delivery, retries the ones that fail, and keeps a delivery history you can open from the dashboard.

  • Signed deliveries
  • Automatic retries
  • Delivery history
A signed image_stored webhook delivery as your endpoint receives it.
POST https://api.example.com/hcti

webhook-id: msg_be4c51.rK4mQ9

webhook-timestamp: 1792617600

webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

x-hcti-attempt: 1

{
  "type": "image_stored",
  "occurred_at": "2026-10-30T14:00:00Z",
  "data": {
    "image_id": "be4c5118-7a0e-4f3b-9d1f",
    "location": "customer_storage",
    "width": 2400, "height": 1260,
    "file_size_bytes": 418233
  }
}
Your handler 200 OK · 84 ms

image_stored

Find out when the file is actually there

An image request returns its URL immediately, and the image renders the first time that URL is loaded. Instead of polling the URL, wait for image_stored.

You get one event per place the file was saved: HCTI storage, and your own bucket if the request used a storage destination. That includes Open Graph images rendered by a crawler's first visit and every image in a batch.

  1. 0s

    POST /v1/image

    Returns the image URL right away

  2. …

    GET https://hcti.io/v1/image/be4c…

    First load renders the image

  3. ✓

    Saved to your R2 bucket

    And to HCTI storage, unless you turned it off

  4. →

    POST https://api.example.com/hcti

    image_stored, signed

Events

Subscribe to what your app needs to hear about

Update events carry the previous and new value of every property that changed, so you can apply the change without fetching the resource again.

Images
image_deletedimage_stored
Templates
template_createdtemplate_updatedtemplate_deleted
Storage destinations
storage_destination_createdstorage_destination_updatedstorage_destination_deleted
OG configs
og_config_createdog_config_updatedog_config_deleted
Proxies
proxy_createdproxy_updatedproxy_deleted
API keys
api_key_createdapi_key_updatedapi_key_deleted
Organization
organization_plan_changedorganization_usage_resetorganization_usage_alert

Secrets like storage keys and proxy passwords show up as set or not set, never as values. A webhook can only subscribe to events its creator is allowed to read.

The data of a template_updated event.
template_updated
{
  "entity": { "type": "template", "id": "t-0193f6" },
  "changes": {
    "name": {
      "kind": "value",
      "previous_value": "Order receipt",
      "new_value": "Order receipt v2"
    },
    "viewport_width": {
      "kind": "value",
      "previous_value": 1200,
      "new_value": 1600
    }
  }
}

Delivery

If your endpoint is down, the event waits

Timeouts, connection errors, and 408, 429, or 5xx responses are retried on a backoff, up to 5 attempts. A Retry-After header can push the next one later.

organization_usage_alert → Ops alerts

Succeeded on attempt 4
  1. 14:02

    503

  2. +5 min

    503

  3. +1 hr

    timeout

  4. +12 hr

    200

  5. +2 days

    not needed

Every attempt carries the same webhook-id, so your handler can drop a duplicate. The dashboard keeps the exact payload and each attempt's status and timing.

Verify with any Standard Webhooks library. Deliveries are signed with HMAC-SHA256 over the id, timestamp, and body, in the Standard Webhooks format.

Test before real traffic. Saving a new endpoint sends a webhook_test event, and Send test in the dashboard sends another whenever you need one.

Endpoints

Send events to your API or a no-code workflow

Any HTTPS URL works: your own service, a serverless function, or a catch hook in an automation tool.

Start a Zapier or Make scenario when an image is stored, or post usage alerts into the tool your team already watches. Add the headers or query parameters the receiver expects, and mark credentials as secret so they're encrypted and never returned.

Free plans include 2 webhooks per organization; paid plans include 10. Compare plans

Custom headers and query parameters on a webhook.

Parameters

Header Authorization •••••••••••••••• Secret
Header X-Environment production Plain
Query token •••••••• Secret
Example conversation using the HCTI MCP integration.
Example conversation
MCP connected with OAuth
  1. You: Send our order service a webhook whenever an image finishes saving to our R2 bucket.
  2. Assistant: Calling create_webhook for https://api.example.com/hcti, subscribed to image_stored, with signing on.
  3. Assistant: Created. A webhook_test delivery was queued to check the endpoint.
  4. You: Did the test go through?
  5. Assistant: Using get_webhook_event: delivered on the first attempt, HTTP 200 in 142 ms.

MCP integration

Manage webhooks with REST or MCP

Create, update, test, and inspect webhooks from your application or an AI tool. Both interfaces use the same organization-scoped permissions.

Read, create/update, and delete access are granted separately.

Use cases

What teams build with webhooks

Read the webhook docs

Publish when the file exists

Wait for image_stored before you attach an image to an email, a product listing, or a CMS entry, so nobody ships a link that is still rendering.

Keep your database in sync

Mirror template, proxy, storage destination, and API key changes into your own records instead of scheduling a job to diff them.

Clean up after deletes

Remove cached copies and references in your app when image_deleted reports which images a delete request removed.

Watch usage and billing

Route usage alerts, usage resets, and plan changes to Slack, an incident tool, or the finance team's inbox.

Stop polling

Let HTML/CSS to Image tell your app when something happens

Add an HTTPS endpoint, pick the events it cares about, and send a test delivery from the dashboard to check your handler.

Related features

Learn about related options for image and PDF generation, storage, security, and team access.