> ## Documentation Index
> Fetch the complete documentation index at: https://www.logo.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Logo.dev serves logos and brand data for companies that already exist. It does not design logos. If someone wants a logo designed, say so and stop.
> When a message is only a brand name, domain, ticker, ISIN, or crypto symbol, reply with https://www.logo.dev/search?q=<url-encoded term> and the matching img.logo.dev call. Never assert whether a specific brand is in the index.
> Image URLs come from img.logo.dev with a publishable key in the token query parameter: https://img.logo.dev/:domain?token=LOGO_DEV_PUBLISHABLE_KEY, also /name/:brand, /ticker/:symbol, /crypto/:symbol, /isin/:isin. JSON comes from api.logo.dev/v2 with a secret key (sk_) in an Authorization: Bearer header, server-side only.
> Product names: the Logo CDN serves images from img.logo.dev. The Logo API is the Brand API and the Search API, which return JSON from api.logo.dev/v2. The Transaction Enrichment API (beta) is a separate service at https://www.logo.dev/api/transaction with its own beta key. Credits is the Logo API billing unit. Never recommend the deprecated v1 routes (/search, /brand, /describe, /logo/*) or the Describe API. Use /v2/brands and /v2/search.
> Report a wrong, missing, or outdated logo at https://www.logo.dev/update. Email support@logo.dev for anything broken and sales@logo.dev for volume pricing and contract terms.
> MCP setup is at https://www.logo.dev/docs/mcp/introduction and webhooks at https://www.logo.dev/docs/webhooks.

# Webhooks

> Get webhook events when a brand you follow changes, or when a brand that returned 202 finishes indexing. Manage endpoints, signing secrets, and brand subscriptions.

Webhooks send an HTTP request to your server when a brand changes, or when a lookup that returned `202` finishes. You don't have to poll.

## Who can use webhooks

Webhooks are available on all paid plans. They belong to your team, so call the webhook routes with one of your team's secret keys.

Owners and admins can create, update, and delete endpoints, and rotate signing secrets. Other members can list endpoints and read their secrets.

On the free plan, or without the right role, the webhook routes return `403` with code `forbidden`. See [pricing](https://www.logo.dev/pricing) for what each plan includes.

Delivery has two parts:

* **Endpoints** decide where events go. Manage them in the dashboard or through `/v2/webhook_endpoints`.
* **Subscriptions** decide which brands send you `brand.updated` events. Manage them by brand id through `/v2/brands/{id}/subscription`.

Endpoints and subscriptions are free and have no rate limit. Each plan caps how many you can have. See [plan limits](#plan-limits).

## Keep your data current

<Steps>
  <Step title="Create an endpoint">
    Create an [endpoint](#endpoints), read its signing secret, and set your receiver to [verify deliveries](#verify-deliveries).
  </Step>

  <Step title="Look up a brand">
    Send a lookup such as `GET /v2/brands?domain=apple.com`. On `200`, store `data.id` next to your own record, download the images you need, and save their `etag` values. Several of your records can point to the same brand id.
  </Step>

  <Step title="Handle a 202">
    Keep `metadata.request_id`. When a `brand.indexed` event with that `request_id` arrives, fetch `data.brand_id` if `status` is `ready`. Stop retrying if `status` is `not_found`.
  </Step>

  <Step title="Handle brand.updated">
    Find your records by the event's `data.brand_id`, and fetch `GET /v2/brands/{id}?nosubscribe=true`. Update your records, and download again only the images whose `etag` changed.
  </Step>

  <Step title="Stop updates you don't need">
    Send `DELETE /v2/brands/{id}/subscription`, and add `nosubscribe=true` to later lookups of that brand.
  </Step>
</Steps>

Keep your own map from identifiers to brand ids. A refetch by id reports an id lookup, not the identifier you first sent.

## Events

Every event has the same outer shape. An event tells you what happened, not what the brand looks like now. Fetch the brand for its data.

| Field | Meaning |
| - | - |
| `object` | Always `event` |
| `id` | The event's `evt_` id. A retried delivery keeps its id, so use it to skip duplicates. |
| `type` | `brand.updated` or `brand.indexed` |
| `created_at` | When Logo.dev detected the change or finished the lookup |
| `data` | The fields for this event type, below |

### Brand updated

Logo.dev sends `brand.updated` when a brand you subscribe to changes.

```json theme={null}
{
  "object": "event",
  "id": "evt_01k6z9a3b4c5d6e7f8g9h0j1k2",
  "type": "brand.updated",
  "created_at": "2026-09-27T12:00:00Z",
  "data": {
    "brand_id": "brand_4x0wr3g0000g00400000000004",
    "changed_attributes": ["name", "logo", "logo.url", "logo.etag", "logo.blurhash", "logo.source", "logo.last_updated_at"]
  }
}
```

`changed_attributes` lists what changed, in a fixed order and without repeats. It holds names and paths, never old or new values. A nested change lists the parent field, then each changed path under it. It can contain:

* Brand fields: `name`, `slug`, `description`, `domain`, and `colors`
* Logo paths: `logo`, `logo.url`, `logo.etag`, `logo.blurhash`, `logo.source`, and `logo.last_updated_at`
* Brandmark paths: `brandmark` and the same five paths under it
* `social_banners`, as one path. Read the whole array again, because no path names which banner changed.
* Social paths: `socials` and `socials.<network>`, for `facebook`, `github`, `instagram`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `tumblr`, `twitter`, `wikipedia`, or `youtube`
* `aliases`, `industry`, and `country_code`. Logo.dev tracks these, but the brand object does not return them yet.

Fetch `GET /v2/brands/{id}?nosubscribe=true` for the current record. It can include changes newer than the event.

### Brand indexed

`brand.indexed` tells you that a lookup which returned `202` has finished. You don't subscribe to it. Logo.dev sends it for your own `202` responses when `metadata.pending.webhook` was `true`.

```json theme={null}
{
  "object": "event",
  "id": "evt_01k6z9b7c8d9e0f1g2h3j4k5m6",
  "type": "brand.indexed",
  "created_at": "2026-09-27T12:01:00Z",
  "data": {
    "request_id": "request_01k6z8m2a4b5c6d7e8f9g0h1j2",
    "status": "ready",
    "brand_id": "brand_4x0wr3g0000g00400000000004",
    "resolution": { "type": "domain", "value": "stripe.com", "method": "direct", "context": {} }
  }
}
```

| Field in `data` | Meaning |
| - | - |
| `request_id` | The `request_id` of the `202` response this event answers |
| `status` | `ready` when the brand is indexed, `not_found` when the lookup found nothing |
| `brand_id` | The brand to fetch. Present only when `status` is `ready`. |
| `resolution` | The [resolution](/docs/brand/introduction#resolution) from the `202` response. `context` is always `{}` on events. |

On `ready`, fetch the brand by id. A normal fetch also subscribes you to the brand, and `nosubscribe=true` skips that. On `not_found`, the original lookup now returns `404`.

## Verify deliveries

Logo.dev signs deliveries and sends them through Svix. Verify each signature with your endpoint's signing secret before you act on the event. [Svix's receiving guide](https://docs.svix.com/receiving/introduction) shows how, and also covers retries and replays.

## Endpoints

An endpoint is an HTTPS URL that receives events. Endpoints belong to your team, and the dashboard shows the same endpoints.

```bash theme={null}
curl --request POST "https://api.logo.dev/v2/webhook_endpoints" \
  --header "Authorization: Bearer LOGO_DEV_SECRET_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "url": "https://hooks.example.com/logo-dev", "filter_types": ["brand.updated"] }'
```

Leave out `filter_types`, or send `[]`, to receive every event type. The response does not include the signing secret. Read it from the secret route.

Any team member can list and read endpoints and secrets. Creating, changing, deleting, and rotating need the owner or admin role.

When you rotate a signing secret, the old secret keeps working for a grace period. Deploy the new one during that time so you don't drop deliveries. Treat the secret as a credential.

| Task | Reference |
| - | - |
| Create an endpoint | [Create a webhook endpoint](/docs/api-reference/webhook-endpoints/create-a-webhook-endpoint) |
| List or read endpoints | [List webhook endpoints](/docs/api-reference/webhook-endpoints/list-webhook-endpoints), [Get a webhook endpoint](/docs/api-reference/webhook-endpoints/get-a-webhook-endpoint) |
| Change or pause an endpoint | [Update a webhook endpoint](/docs/api-reference/webhook-endpoints/update-a-webhook-endpoint) |
| Delete an endpoint | [Delete a webhook endpoint](/docs/api-reference/webhook-endpoints/delete-a-webhook-endpoint) |
| Read or rotate the signing secret | [Get a webhook signing secret](/docs/api-reference/webhook-endpoints/get-a-webhook-signing-secret), [Rotate a webhook signing secret](/docs/api-reference/webhook-endpoints/rotate-a-webhook-signing-secret) |

## Subscriptions

Logo.dev sends `brand.updated` events only for brands your account subscribes to. Your account has one subscription per brand, however you found the brand. You subscribe in two ways:

* **Automatically.** With webhooks on, a `GET /v2/brands` or `GET /v2/brands/{id}` lookup subscribes you to the brand it returns. The lookup reports the result in [`metadata.subscription`](/docs/platform/conventions#the-response-envelope). At your plan's subscription limit, the lookup still returns the brand, but it does not subscribe you, and `metadata.subscription.status` is `quota_exceeded`. Add `nosubscribe=true` to skip the subscribe. It does not remove a subscription you already have.
* **Explicitly.** Send `PUT /v2/brands/{id}/subscription`.

Logo and brandmark lookups, search, and image downloads never subscribe you.

A subscription follows the brand, not the identifier that found it. If a domain or ticker later matches a different brand, your subscription stays with the first one. Look your identifiers up again to catch that.

Deleting a subscription stops events for every record of yours that points to the brand. A later lookup subscribes you again, unless it sends `nosubscribe=true`.

| Task | Reference |
| - | - |
| Check a subscription | [Get a brand subscription](/docs/api-reference/subscriptions/get-a-brand-subscription) |
| Subscribe | [Subscribe to a brand](/docs/api-reference/subscriptions/subscribe-to-a-brand) |
| Stop events | [Unsubscribe from a brand](/docs/api-reference/subscriptions/unsubscribe-from-a-brand) |

## Plan limits

Each plan caps the number of webhook endpoints and the number of subscribed brands on your account. The [pricing page](https://www.logo.dev/pricing#compare) lists the caps for each plan. A disabled endpoint still counts toward the cap.

At the cap, `POST /v2/webhook_endpoints` and `PUT /v2/brands/{id}/subscription` return `409` with the code [`limit_exceeded`](/docs/platform/errors#logo-api-status-codes). The message names your plan's limit. To add another, delete an endpoint or unsubscribe from a brand, or upgrade your plan. A brand you already subscribe to stays subscribed at the cap.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.