The Pokemon TCG restock API for shops, apps and AI agents
The same real-time restock alerts our app subscribers get, as data you can build on: what just came in stock and where, its price against MSRP, and when each retailer tends to restock. Connect an AI agent over the Model Context Protocol, call it from your own code, or receive signed webhooks on your server.
- Real time. On the Live tier, alerts arrive about a second after they're detected — the same moment our own app subscribers get them, with no holdback. The free Reference tier returns the same rows five minutes later.
- Built for agents. Alerts carry a retailer link, price, MSRP and markup where we have them, and a
confidenceflag so your agent can tell a confirmed sighting from an unverified report. - Three ways to consume. Ask what's in stock now, hold a long-poll until the next matching drop, or get a signed webhook POST.
- Coverage. Target, Walmart, Best Buy, Amazon, Pokémon Center, Sam's Club, Hot Topic, GameStop, Costco and Macy's.
Get a free key Live tier — $29.99/month
Free keys are issued instantly. No sales call, no waiting.
Who it’s for
Card shops and sellers
Price your shelf against retail with get_product_msrp and the markup on every listing. See which big-box retailers have been restocking and when with get_retailer_stats, so you can plan your own stock and events. Put a “restocked at retail” feed on your site or Discord to keep collectors coming back to you.
Developers and bots
Add restock alerts to your app, Discord bot or site without building a monitor. Hold a long-poll for the next matching drop, or have us push each alert to your server as a signed webhook. Alerts carry a cleaned retailer link, price, MSRP and markup where we have them, and a confidence flag.
AI agents
Add one MCP server and your assistant can answer “is anything in stock?”, wait for a specific product at MSRP, or tell whether a price is fair. Setup takes a couple of minutes in Claude Code, Cursor, VS Code or Claude Desktop.
Creators and communities
Run a restock feed for your followers on a Live key, shown at least five minutes after each alert (real-time for an audience is available by agreement).
How it works
| Endpoint | https://bujusjuj.us/mcp |
|---|---|
| Protocol | MCP over Streamable HTTP (JSON-RPC 2.0). Protocol versions 2025-06-18 and 2025-03-26. Stateless: every request stands alone. |
| Auth | Authorization: Bearer bjs_… on every tool call. initialize, tools/list and ping also answer without a key, so you can explore the server before signing up. |
| Tools | 10 — see Tools |
| Webhooks | Optional, HMAC-SHA256 signed — see Webhooks |
Quickstart
1. Get a key
Go to bujusjujus.com/api-access, enter your email, and open the link we send you. Your developer console is there: take a free Reference key straight away, or subscribe to Live. You don't need an account first — we'll make one.
A key looks like bjs_ followed by 43 characters. It is shown once; we only store a hash, so save it somewhere safe. Lost one? Revoke it in the console and mint another.
2. Connect your agent
Claude Code
claude mcp add --transport http bujusjujus https://bujusjuj.us/mcp \ --header "Authorization: Bearer $BUJUS_API_KEY"
Any MCP client that supports remote HTTP servers with custom headers (the exact file and key names vary by client):
{
"mcpServers": {
"bujusjujus": {
"type": "http",
"url": "https://bujusjuj.us/mcp",
"headers": { "Authorization": "Bearer bjs_YOUR_KEY" }
}
}
}
For a client that only runs local (stdio) servers, bridge it with npx mcp-remote https://bujusjuj.us/mcp --header "Authorization: Bearer bjs_YOUR_KEY".
Step-by-step instructions for Claude Desktop, Claude Code, Cursor and VS Code are in the setup guide. Runnable examples (Node, Python webhooks, a Discord feed) are on GitHub.
3. Ask
"What Pokemon products are in stock right now?" · "Tell me the moment a Pitch Black Elite Trainer Box drops at MSRP." · "Is $65 a fair price for that box?"
Or call it directly
Every call is a JSON-RPC POST. Tool results come back as JSON text in result.content[0].text.
curl -s https://bujusjuj.us/mcp \
-H "Authorization: Bearer $BUJUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_in_stock_now","arguments":{"minutes":30}}}'
get_in_stock_now above is a Live tool. On a free Reference key it answers tier_required — try list_recent_drops instead, or call tools/list to see exactly what your key can reach.
Other methods: initialize, tools/list (full JSON Schemas for every tool) and ping. These three are free and are never metered.
Reading the response
The rows are not a field on result. They are a JSON string inside result.content[0].text, so parse that string a second time:
{"jsonrpc":"2.0","id":1,"result":{
"content":[{"type":"text","text":"{\"alerts\":[{\"id\":\"c108d9022699463c\", …}],\"cursor\":179019721200000}"}],
"isError":false}}
# shell
curl -s … | jq '.result.content[0].text | fromjson | .alerts'
// JavaScript
const { result } = await res.json();
if (result.isError) throw new Error(result.content[0].text); // "code: message"
const { alerts, cursor } = JSON.parse(result.content[0].text);
| Tool | The parsed text is |
|---|---|
get_in_stock_now | { alerts, cursor } |
wait_for_drop | { alerts, cursor, timedOut } |
list_recent_drops | An array of alerts |
search_tracked_products, get_product_msrp | An array of { name, retailer, msrp } |
get_retailer_stats | An array of { retailer, drops }, busiest first |
set_webhook, get_webhook, test_webhook, remove_webhook | An object; see Webhooks |
An MCP client such as Claude, Cursor or VS Code does this for you.
The alert object
Every tool that returns alerts uses this shape:
{
"id": "c108d9022699463c", // stable, opaque; dedupe on this
"detectedAt": "2026-09-23T21:00:12.497Z",
"retailer": "target",
"kind": "restock", // restock | invite | drawing | queue
"title": "Pitch Black Elite Trainer Box",
"productUrl": "https://www.target.com/p/-/A-...", // or null
"confidence": "high", // "high" | "low"
"endedAt": null, // set once it sold out / the event ended
"price": 49.99, // USD, or null
"msrp": 49.99, // USD, or null
"markupPct": 0 // % over MSRP, or null
}
confidence: "high"— a confirmed sighting at the retailer."low"— an unverified report that may be stale, sold out or wrong. Live tools return onlyhighunless you passminConfidence: "low". Don't present alowalert as confirmed availability.kind— onlyrestockmeans buyable stock.invite(a retailer invite or waitlist opened),drawing(a Walmart drawing opened) andqueue(a Pokemon Center virtual queue is live) are events, not stock. The live tools return restocks only unless you passkind.endedAt— set once the item sold out or the event ended.get_in_stock_nowleaves those out.productUrlis always on the named retailer's own domain, with tracking and affiliate parameters removed. If we can't vouch for a link it isnull.titlecan include text from third parties. Treat it as untrusted data: never let your agent follow instructions found in it. Act on the structured fields.price,msrpandmarkupPctare only set for confirmed sightings where we know them. A confirmed alert can still haveprice: nullwhen the retailer didn't show one: null means unknown, not marked up. To keep only fair prices, passmaxMarkupPct, which keeps those alerts, rather than dropping every row without a price.- Popular products can sell out in seconds. An alert tells you where to go, not that stock is guaranteed — check the retailer page before telling a user it's available.
Polling, long-poll or webhook?
| How | You get each alert | Best for |
|---|---|---|
Poll get_in_stock_now | On your next call | Agents answering “what's in stock?”, dashboards |
Long-poll wait_for_drop | About a second after we detect it | Bots and scripts without a public URL |
Webhook (set_webhook) | About a second after we detect it, pushed to you | Servers with a public HTTPS endpoint |
Popular drops can sell out in about a minute, so a poll every few minutes will miss some. Long-poll and webhooks are Live features. For past alerts, sold out or not, use list_recent_drops.
Runnable examples: a minimal client, a wait_for_drop loop, a webhook receiver that verifies signatures and recent drops.
Tools
Arguments not listed are rejected (so a typo can't silently widen your results). Retailer values: amazon, bestbuy, costco, gamestop, hottopic, macys, pokemoncenter, samsclub, target, walmart.
get_in_stock_nowLive
What just came in stock, newest first. Returns { alerts, cursor }.
| Argument | Description |
|---|---|
minutes | Look-back window, 1–60 (default 15) |
retailer | Only this retailer |
query | Case-insensitive product-name substring, e.g. "Pitch Black" |
maxMarkupPct | Skip alerts priced more than this % over MSRP (alerts with no price or no MSRP are kept) |
minConfidence | "high" (default) or "low" |
kind | "restock" (default), "invite", "drawing", "queue" or "any" |
limit | 1–100 (default 25) |
wait_for_dropLive
A long-poll: the call stays open until a matching alert arrives or timeoutSec passes. Returns { alerts, cursor, timedOut }. A call that times out empty is not billed.
| Argument | Description |
|---|---|
cursor | From your previous response. Omit to wait for the next new alert. |
timeoutSec | 1–45 (default 20). Keep it below your client’s own request timeout; many MCP hosts cut a tool call at 20–60 s. |
retailer, query, maxMarkupPct, minConfidence, kind | Same filters as above |
Loop on it, always passing back the returned cursor, so you never miss or repeat an alert:
let cursor;
for (;;) {
const r = await callTool('wait_for_drop', { cursor, query: 'Pitch Black', maxMarkupPct: 10, timeoutSec: 20 });
cursor = r.cursor;
for (const alert of r.alerts) act(alert); // dedupe on alert.id
}
Up to 5 waits per key can be open at once.
timeoutSec is not a delay: a matching alert comes back about a second after we detect it, whatever the timeout. It is only how long one call may stay open. If your client gives up on a call first, you lose nothing — call again with the same cursor and anything that arrived in between is returned. Running a server-side bot? Use set_webhook and we push each alert to you instead.
list_recent_dropsFree
Alert history, newest first, all kinds. retailer, sinceHours (1–2160, i.e. up to 90 days), minConfidence, kind, limit (1–200, default 50).
search_tracked_productsFree
The product catalogue we track, with MSRP. query, retailer, limit. Returns { name, retailer, msrp }. This is the tracked list, not a stock check.
get_product_msrpFree
MSRP for products matching query (required). Products without a known MSRP are left out, so an empty result means "unknown", not "free". Use specific names ("Pitch Black Elite Trainer Box") — a short query also matches packs and bundles.
get_retailer_statsFree
Drop counts per retailer over days (1–90, default 30), busiest first. Includes unverified reports, so read it as relative activity.
set_webhook · get_webhook · test_webhook · remove_webhookLive
Register, inspect, test or remove your webhook. See Webhooks below.
Webhooks
For a server-side bot that shouldn't hold a connection open. Register one HTTPS endpoint per key:
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"set_webhook",
"arguments":{"url":"https://hooks.example.com/bujus","query":"Pitch Black","maxMarkupPct":10}}}
The response includes a signing secret (whsec_…), shown once. Calling set_webhook again replaces the URL and filters and issues a new secret. get_webhook shows status, failures and the last error (never the secret).
What we send
POST https://hooks.example.com/bujus
Content-Type: application/json
User-Agent: bujusjujus-alerts-webhook/1
X-Bujus-Signature: t=1790197212,v1=5b3f…
{"type":"alert","cursor":179019721200000,"sentAt":"2026-09-23T21:00:13.004Z","alert":{ …alert object… }}
- Respond with any
2xxwithin 5 seconds. Redirects are not followed. typeis"alert"for an alert and"test"for a test delivery. Act only on"alert"; answer2xxto any other type and ignore it, so new event types never break your receiver.- A failed delivery is retried after about 5 s, 30 s and 2 min. After 20 consecutive failed deliveries the webhook is disabled; call
set_webhookto re-enable it. - Your URL must be public
httpson port 443. Private, loopback and internal addresses are refused. - Deliveries to one webhook are sent one at a time, in order. If your endpoint falls more than 50 alerts behind, the oldest are dropped.
- You can change your webhook up to 10 times an hour. The signing secret is returned to whatever called
set_webhook— if that was an AI assistant, the secret is in its conversation, so rotate it from a script before going to production.
Test your endpoint
Don't wait for a drop to find out whether your receiver works. Right after set_webhook, call test_webhook: we send one signed POST to your URL straight away and tell you what it answered.
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"test_webhook","arguments":{}}}
// result, once parsed:
{"delivered":true,"status":204,"error":null,"tookMs":184,
"sentAt":"2026-10-28T17:30:00.000Z","url":"https://hooks.example.com/bujus","active":true}
The body is {"type":"test","sentAt":…,"note":…}, signed with your secret like a real delivery, and has no alert field, so a receiver that acts on alert won't post anything. When it fails you get delivered: false, the status or error, and a hint saying what to fix. A test isn't billed or retried, doesn't count toward the 20-failure limit, and can run 10 times an hour. It works on a disabled webhook too, so you can check a fix before re-enabling it with set_webhook.
Verify every request
The signature is HMAC-SHA256(secret, "<t>.<raw body>") as hex. Use the raw request bytes, compare in constant time, and reject a t more than 5 minutes old.
// Node.js
const crypto = require('crypto');
function verify(secret, rawBody, header) {
const p = Object.fromEntries(header.split(',').map(kv => kv.split('=')));
if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const want = crypto.createHmac('sha256', secret).update(`${p.t}.${rawBody}`).digest();
const got = Buffer.from(p.v1 || '', 'hex');
return got.length === want.length && crypto.timingSafeEqual(got, want);
}
# Python
import hmac, hashlib, time
def verify(secret: str, raw_body: bytes, header: str) -> bool:
p = dict(kv.split("=", 1) for kv in header.split(","))
if abs(time.time() - int(p.get("t", 0))) > 300:
return False
want = hmac.new(secret.encode(), p["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(want, p.get("v1", ""))
Limits
| Limit | Value |
|---|---|
| Daily quota | Live: 20,000 tool calls per UTC day. Reference: 5,000. Webhook deliveries count; empty wait_for_drop timeouts, initialize, tools/list and ping don't. |
| Per key | 120 requests per minute |
| Per IP | 600 requests per minute with a key; 20 per minute without one |
| Concurrent waits | 5 open wait_for_drop calls per key |
| Request size | 64 KB |
When limited you get HTTP 429 with a Retry-After header. Back off and retry.
Errors
A request refused before any tool runs gets an HTTP error status and a JSON-RPC error object. Where there is one, error.data.error names the case.
| HTTP | Meaning | What to do |
|---|---|---|
400 | The body isn't valid JSON (-32700), or the MCP-Protocol-Version header names a version we don't support (-32600; error.data.supported lists ours) | Send Content-Type: application/json and one JSON-RPC object. Use 2025-06-18 or 2025-03-26, or leave the header out. |
401 | missing_key, invalid_key or key_revoked | Send Authorization: Bearer bjs_… with exactly one space. A revoked key can't come back: create a new one in your console. |
403 | account_inactive: the account behind the key is no longer active. Or Origin not allowed: the request came from a web page on another site. | Email us about the account. Call the API from your server, not a browser; keys must stay server-side. |
405 | A GET or DELETE on /mcp | Use POST. There is no SSE stream and no session to close. |
413 | The body is over 64 KB | Send one JSON-RPC request per call. |
429 | A rate limit, or the daily quota (error.data then has used, limit and resetsAt) | Wait Retry-After seconds, then retry. See Limits. |
500 | Something failed on our side | Retry. If it keeps happening, email us with the time. |
A malformed call that gets past those checks returns HTTP 200 with a JSON-RPC error: -32601 for an unknown method, -32602 when tools/call has no name or its arguments isn't an object, -32600 for an id that isn't a string, number or null.
A tool that runs but refuses returns HTTP 200 with result.isError: true and a text of the form code: message. Refused calls are not billed.
| Code | Meaning and fix |
|---|---|
tier_required | A Live tool called with a free Reference key. list_recent_drops, search_tracked_products, get_product_msrp and get_retailer_stats work on every key; Live adds the rest. |
invalid_arguments | An argument is unknown, the wrong type or out of range, and the message names it. Unknown arguments are refused on purpose, so a typo can't silently widen your results. tools/list has every schema. |
unknown_tool | No tool by that name. Check tools/list. |
too_many_waits | More than 5 open wait_for_drop calls on one key. Run one wait loop per key. |
busy | We're at long-poll capacity. Retry after a few seconds. |
invalid_webhook | The webhook URL was refused (it must be public https on port 443), or it changed more than 10 times in an hour. |
tool_failed | The tool failed on our side. Retry; email us if it keeps happening. |
Troubleshooting
Most “it's broken” reports turn out to be one of these. Your developer console shows, for each key, the last 24 hours of calls and how many returned rows, the alerts the key was sent, which clients used it and how its webhook is doing. It's the quickest way to see what we sent you.
Every call returns no rows
- Check where you read the rows. They are a JSON string in
result.content[0].text; see Reading the response. A client that looks forresult.alertsfinds nothing, every time. - Empty is usually correct.
get_in_stock_nowreturns only restocks from the lastminutes(default 15, at most 60) that haven't sold out, and by default only confirmed restocks (kind: "restock",minConfidence: "high"). Most drops happen overnight US time and sell out within minutes, so a daytime call is normally empty. - Prove the pipe works. Call
list_recent_dropswith{"sinceHours": 72}. It returns past alerts, sold out or not. If rows come back, your key and your parsing work, and the empty calls were real. - Check your own filters. Log the row count before you filter. A filter that drops alerts with no
pricedrops every alert whose price we couldn't see.
curl -s https://bujusjuj.us/mcp \
-H "Authorization: Bearer $BUJUS_API_KEY" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_recent_drops","arguments":{"sinceHours":72}}}' \
| jq -r '.result.content[0].text | fromjson | .[] | [.detectedAt, .retailer, .title, .price] | @tsv'
An alert has no price
price: null on a confirmed alert means the retailer didn't show us a price, not that the item is marked up. To keep only fair prices, pass maxMarkupPct (for example 0): it skips alerts priced over MSRP and keeps alerts with no price.
I missed a drop, or my alerts arrive late
- Popular drops can sell out in about a minute, so a poll every few minutes misses some however fast we are.
wait_for_dropand webhooks hand you each alert about a second after we detect it; see Polling, long-poll or webhook? - A Reference key gets every alert five minutes after detection. That is the free tier working as designed.
- Unverified community reports (
confidence: "low") can arrive a little later than confirmed sightings.
The same alert comes back on every call
That's expected: get_in_stock_now keeps returning an alert while the item is in stock, so deduplicate on id. When it sells out the alert gets an endedAt and leaves get_in_stock_now. A later restock of the same product is a new alert with a new id.
wait_for_drop comes back empty, or my client cuts it off
"timedOut": true with no alerts means nothing matched within timeoutSec, and it isn't billed; call again straight away with the returned cursor. Keep timeoutSec below your client's own request timeout. If your client gives up first you lose nothing: call again with the same cursor.
My webhook gets nothing
- Call
test_webhook. It sends a signed test POST and tells you at once what your endpoint answered, with ahintwhen it fails. - Call
get_webhook: it showsactive,consecutiveFailures,lastDeliveredAtandlastError. Your console shows the same. - A webhook receives alerts that happen after you register it, so
lastDeliveredAt: nullon a quiet day is normal. - Webhooks send confirmed restocks unless you widen them: pass
kindorminConfidencetoset_webhook. - The URL must be public
httpson port 443 and answer2xxwithin 5 seconds. Redirects aren't followed, so a301or302counts as a failure; register the final URL. - After 20 failed deliveries in a row the webhook is disabled. Fix the endpoint, then call
set_webhookagain to re-enable it (you get a new secret). - Signature never matches? Compute the HMAC over the raw request bytes. A framework that parses and re-serialises the JSON changes the bytes.
Is it you or us?
GET https://bujusjuj.us/health answers {"ok":true,"service":"alerts-api","fresh":true}. "fresh": false means our feed is behind and new alerts will wait until it catches up. If that lasts more than a few minutes, tell us.
Still stuck? What to send us
Email [email protected] with:
- your key's prefix: the first 12 characters, as your console shows them (never the whole key);
- the time window, with its time zone;
- the tool and the arguments you sent;
- what you expected, and what you got.
We keep a record of each call your key makes and the alerts it was sent (section 10 of the API Terms), so we can tell you exactly what your key received, and when.
Security
- Keep your key server-side. Never ship it in a browser app or a public repo — the
bjs_prefix makes leaked keys easy to scan for. - Lost or exposed key? Revoke it yourself in your console and mint a replacement — you can create the new one before revoking the old, so there is no gap.
- Treat alert
titletext as untrusted input to your model. - Verify every webhook signature before acting on it.
- Report a vulnerability to [email protected].
Tiers
| Reference — free | Live — $29.99/month | |
|---|---|---|
| Alert data | Same rows, 5 minutes later | The moment we see it, no holdback |
| Tools | list_recent_drops, search_tracked_products, get_product_msrp, get_retailer_stats | Those four plus get_in_stock_now, wait_for_drop and the four webhook tools |
| Webhooks | — | Signed, with retries |
| Calls per day | 5,000 | 20,000 |
| Issued | Instantly | Instantly, on payment |
A five-minute-old restock signal is not a substitute for a live one — Target drops have sold out in around 78 seconds — so Reference is for building and testing against real data, not for running on.
Enter your email, open the link we send, and take a key. Live subscriptions renew monthly until cancelled; cancel any time at bujusjujus.com/account-help and the key keeps working until the end of the paid period.
The API is sold for business use. Issuing a key is automatic and implies no approval or endorsement of what you build — use of it is governed by the Alerts API Terms and the Terms of Service, and you are responsible for how you use the data, including following each retailer's terms and purchase limits.
FAQ
Is there a free tier?
Yes. Reference keys are free and issued instantly with no card. They return the same alerts five minutes later, with four tools and 5,000 calls a day. They are for evaluation and development; a product your users rely on needs a Live key.
How fast is the Live tier?
A Live key gets each alert about a second after we detect it, the same moment our own app subscribers are notified. There is no holdback.
Can I show the data to my customers or followers?
Yes, in your own products, with one rule: anything people outside your organization see must be at least five minutes past the alert’s detectedAt time. Real-time alerts for your audience need a separate agreement; email [email protected].
Which AI apps work with it?
Any MCP client that can connect to a remote HTTP server and send an Authorization header, such as Claude Code, Cursor and VS Code. Apps that only run local servers, such as Claude Desktop, connect through the mcp-remote bridge. The setup guide at bujusjujus.com/api-setup has the steps for each.
Can I use it to buy products automatically?
No. The API delivers information, not purchases. Our terms prohibit using the data for automated purchasing or to get around a retailer’s terms or purchase limits.
How long can I keep the data?
Up to 30 days, as long as you need it to run your product, and not as a historical archive. If your licence ends, delete it within 7 days. Section 4 of the Alerts API Terms has the details.
Why does get_in_stock_now return nothing?
Usually because nothing is in stock. It returns only restocks from the last 15 minutes (up to 60) that haven't sold out, and most drops happen overnight US time and sell out within minutes. Call list_recent_drops to see recent alerts, and use wait_for_drop or a webhook to catch the next one. Also check that you parse the rows from result.content[0].text. More in Troubleshooting.
Why does an alert have no price?
price is null when the retailer didn't show us a price for that alert. It means unknown, not marked up. To keep only fair prices, filter with maxMarkupPct, which keeps alerts that have no price.
Can I get higher limits, real-time redistribution or an invoice?
Yes. Email [email protected] with what you are building and we will set it up.
Changelog
Changes you might notice as a client, newest first.
- October 28, 2026. Alert titles and product names no longer contain HTML entities (
&now arrives as&). The docs say a confirmed alert can haveprice: nulland thatmaxMarkupPctkeeps those alerts. Your developer console shows each key's recent activity. New Live tooltest_webhooksends a signed test POST to your webhook and reports the result at once; webhook bodies now come in two types,"alert"and"test". This page gained Troubleshooting, Reading the response and the full error list. - October 25, 2026. We keep a record of each call and of the alerts each key was sent, so support can answer “what did my key receive?” (section 10 of the API Terms).
- October 23, 2026.
initialize,tools/listandpinganswer without a key. The full docs for agents are at /llms-full.txt. Thelist_recent_dropsdescription lists every field it returns. - October 22, 2026. An
initializethat sends a newerMCP-Protocol-Versionheader now negotiates a supported version instead of failing with 400. - September 30, 2026.
wait_for_dropdefaults to a 20-second timeout (45 at most). A call your client hung up on is no longer billed. - September 24, 2026. Self-serve keys at /api-access: a free Reference key straight away, or Live on payment.
- September 23, 2026. Public launch. Alerts carry
kindandendedAt, andproductUrlis always the retailer's own product page.
Support
Email [email protected] — a real person reads it.