# Livestream alerts

This guide builds the part of a donation page that streamers care about\: a banner on the stream the moment a payment arrives, with the donor's name and message. It uses three pieces, all on the payment API\: a [payment](/sdk/typescript/payments) with metadata, an [alert key](/guides/authentication#alert-keys), and the [live feed](/sdk/typescript/feed).

## 1. Carry the name and message on the payment

Your server creates the payment, and the donor's words travel with it as `metadata`. Metadata is bounded (20 keys, 1000 characters a value, 8 KB), so validate and trim what the donor typed first.

```ts
const intent = await mayarin.payment.createIntent({
  merchant: { id: creatorMerchantId, name: "Creator", city: "Jakarta", countryCode: "ID" },
  amount: { amount: "50000.00", asset: "IDR" },
  metadata: {
    donor: name.slice(0, 40) || "Anonymous",
    message: message.slice(0, 280),
  },
});

redirect(intent.checkoutUrl);
```

Donors need no account. The payment is the record.

## 2. Mint an alert key

In the dashboard, open **API keys**, choose **Alert — read-only payment feed**, and copy the `ak_…` key. Mint one per overlay, so you can revoke one without touching the rest. The key can read the feed and nothing else.

## 3. Listen in the overlay page

An OBS browser source is a web page. Give it the alert key and listen\:

```ts
import { createMayarinBrowser } from "@mayarin/sdk/browser";

const mayarin = createMayarinBrowser({ baseUrl: "https://api.mayarin.xyz", alertKey });

for await (const event of mayarin.feed.stream()) {
  if (event.type === "payment.completed") queueAlert(event);
}
```

The sequence reconnects by itself and resumes from the last payment it saw, so an overlay that loses its connection mid-stream still shows every donation. Show alerts one at a time from a queue, and escape `metadata.message` before it goes in the page\: it is text a stranger typed.

## 4. Put it in OBS

Add a **Browser** source in OBS and point it at your overlay page. Give the page its key in the URL **fragment**, `https://overlay.example.com/#key=ak_…`, and read it with `location.hash`. A fragment is never sent to a server, so the key does not reach your access logs or a CDN's. An alert key can only read the feed, so a leak shows what the overlay already shows, but a key that is never logged cannot leak that way at all.

```ts
const alertKey = new URLSearchParams(location.hash.slice(1)).get("key") ?? "";
const mayarin = createMayarinBrowser({ baseUrl: "https://api.mayarin.xyz", alertKey });
```

A `?key=` in the query works too, and is the only form for a tool that cannot run your script (below); then keep your server from logging the query string.

If a tool cannot run your script at all, it can open the feed directly. `GET /v1/payments/stream?key=ak_…` is a plain event stream, documented in the [API reference](/api-reference/payments).

## A history that matches the alerts

An alert is a notification, not a record. For the history page, read the payments with [`payment.list`](/sdk/typescript/payments#list-payments); it returns the same payments, with what each settled for. If your overlay was down, `feed.stream({ since })` replays what it missed.

## If a key leaks

Deactivate it in the dashboard. The next reconnect gets a `401`, and the overlay stops. Mint a replacement and update the page.
