# Marketplace connectors (/integrations/marketplace)



The marketplace is the in-product, installable side of the [integrations ecosystem](https://zumik.ai/integrations). Where the framework integrations are things you import into your code, a marketplace connector is something you **install into a project** so Zumik delivers events to a destination you own.

The first connector class is **event destinations**. Install one and Zumik delivers account events - today, [budget-threshold alerts](/api-v2/billing) at 50/80/100% of a project's monthly budget - to Slack, Discord, or any HTTPS endpoint.

## The catalog [#the-catalog]

```bash
curl https://api.zumik.ai/v2/marketplace/listings \
  -H "Authorization: Bearer $ZUMIK_API_KEY"
```

| Connector      | Category                 | Destination                          |
| -------------- | ------------------------ | ------------------------------------ |
| Slack          | Alerting & notifications | A `hooks.slack.com` incoming webhook |
| Discord        | Alerting & notifications | A `discord.com` channel webhook      |
| Custom webhook | Alerting & notifications | Any HTTPS endpoint you control       |

## Install a connector [#install-a-connector]

Installing is an admin action (it configures an outbound integration for the whole project). Provide the listing slug, an optional label, and the destination URL:

```bash
curl https://api.zumik.ai/v2/marketplace/installations \
  -H "Authorization: Bearer $ZUMIK_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "listing_slug": "slack",
    "name": "ops channel",
    "url": "https://hooks.slack.com/services/T0/B0/XXXX"
  }'
```

The URL is sealed at rest (AES-256-GCM, the same envelope as a BYOK key). The response - and every later read - returns only the destination **host** and a **fingerprint**, never the URL with its secret token:

```json
{
  "id": "mki_...",
  "object": "marketplace_installation",
  "listing_slug": "slack",
  "name": "ops channel",
  "status": "active",
  "destination_host": "hooks.slack.com",
  "secret_fingerprint": "mkfp_8f1c2d3e4a5b",
  "last_delivery_status": null
}
```

You can do this from the console **Marketplace** tab too - it lists the catalog, installs, and manages connectors without the API.

## Test it [#test-it]

Send a synthetic event and get the delivery result back inline, so you can confirm the connector works end to end:

```bash
curl -X POST https://api.zumik.ai/v2/marketplace/installations/mki_.../test \
  -H "Authorization: Bearer $ZUMIK_ADMIN_KEY"
```

```json
{ "ok": true, "status": "delivered_200" }
```

## Manage [#manage]

| Action           | Request                                          |
| ---------------- | ------------------------------------------------ |
| List installs    | `GET /v2/marketplace/installations`              |
| Get one          | `GET /v2/marketplace/installations/{id}`         |
| Enable / disable | `PATCH .../{id}` with `{ "status": "disabled" }` |
| Uninstall        | `DELETE /v2/marketplace/installations/{id}`      |

Disabling stops delivery without losing the configuration; uninstalling drops the record and the sealed URL. Each installation also records its `last_delivery_status` and `last_delivery_at` so you can see whether the most recent event got through.

## Delivery payloads [#delivery-payloads]

Each connector receives the shape its destination expects:

* **Slack** - `{ "text": "..." }`
* **Discord** - `{ "content": "...", "allowed_mentions": { "parse": [] } }`
* **Custom webhook** - a stable JSON envelope:

  ```json
  {
    "source": "zumik",
    "type": "billing.budget.alert",
    "title": "Budget alert: 80% of monthly budget",
    "body": "Project ... crossed 80% of its monthly budget (cycle spend $...).",
    "data": { "project_id": "prj_...", "threshold_pct": 80, "cycle_spend_usd": 41.2 }
  }
  ```

Delivery is best-effort and fire-and-forget: a slow or failing destination never blocks or fails the request that produced the event.

## Security [#security]

The destination URL is a customer-supplied SSRF surface, so install-time validation:

* requires `https`;
* pins the host for the managed connectors (`hooks.slack.com` for Slack, `discord.com` and friends for Discord);
* for the custom webhook, blocks loopback, private (RFC 1918), link-local / cloud-metadata (`169.254.0.0/16`), CGNAT, and IPv6 ULA / link-local destinations, plus obvious internal names (`localhost`, `*.local`, `*.internal`).

This is a boundary check, not a substitute for network egress filtering - a hardened deployment should also egress-filter the delivery path against DNS rebinding. The URL is never logged; delivery errors are recorded coarsely (`timeout`, `connect_error`, `http_500`) so a transport error can't leak the resolved address.
