API reference
Everything in the dashboard is available over HTTPS. Authenticate with a scoped key, send users and events, read stats and manage broadcasts. The OpenAPI document is at /v1/openapi.json.
Quickstart
1. Create a project and an API key (API Keys page). 2. Send a user. 3. Send an event. Both appear in the dashboard within seconds.
export VIBEADMIN_API_KEY=adm_test_...
curl -X POST https://www.easy-admin.solutions/v1/users \
-H "Authorization: Bearer $VIBEADMIN_API_KEY" -H "Content-Type: application/json" \
-d '{"id":"user_123","email":"ada@example.com","name":"Ada","plan":"pro"}'
curl -X POST https://www.easy-admin.solutions/v1/events \
-H "Authorization: Bearer $VIBEADMIN_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"workflow.created","userId":"user_123"}'Prefer to have your coding agent do it? The Get started page generates a prompt for your stack (Next.js, React, Node, Python, Supabase, Firebase).
Authentication
Send Authorization: Bearer <key>. Keys are per environment: adm_test_… for development and preview, adm_live_… for production. A key can only ever see its own environment and only the scopes it was given (users.read, users.write, users.delete, analytics.read, events.write, broadcast.read, broadcast.create, broadcast.send). Keep keys on your server: never ship them in browser code.
REST API
Full machine-readable spec: /v1/openapi.json (OpenAPI 3.1). Users are keyed by your own id; omitted fields stay unchanged, metadata keys merge, and null removes a key.
| Endpoint | What it does | Scope |
|---|---|---|
| GET/v1/users | List users | users.read |
| POST/v1/users | Create or update a user | users.write |
| POST/v1/users/bulk | Upsert up to 500 users | users.write |
| GET/v1/users/{id} | Get a user | users.read |
| PATCH/v1/users/{id} | Update a user | users.write |
| DELETE/v1/users/{id} | Delete a user | users.delete |
| POST/v1/users/{id}/suspend | Suspend a user | users.write |
| POST/v1/users/{id}/restore | Restore a suspended user | users.write |
| GET/v1/broadcasts | List broadcasts | broadcast.read |
| POST/v1/broadcasts | Create a draft broadcast | broadcast.create |
| GET/v1/broadcasts/{id} | Get a broadcast | broadcast.read |
| POST/v1/broadcasts/{id}/send | Send a draft broadcast | broadcast.send |
| GET/v1/events | List events | analytics.read |
| POST/v1/events | Send events | events.write |
| GET/v1/event-definitions | List discovered event names | analytics.read |
| GET/v1/stats | Headline statistics | analytics.read |
| POST/v1/webhooks/{id} | Inbound webhook | — |
# list users on the pro plan
curl "https://www.easy-admin.solutions/v1/users?plan=pro&limit=20" -H "Authorization: Bearer $VIBEADMIN_API_KEY"
# bulk upsert (up to 500)
curl -X POST https://www.easy-admin.solutions/v1/users/bulk -H "Authorization: Bearer $VIBEADMIN_API_KEY" \
-H "Content-Type: application/json" -d '{"users":[{"id":"a","email":"a@x.co"},{"id":"b","email":"b@x.co"}]}'JavaScript / TypeScript SDK
@vibeadmin/sdk wraps the API with batching, retries and idempotency for events. It has not been published to npm yet; until then, use the REST examples above (the SDK lives in the repo under packages/sdk).
import { AdminClient } from "@vibeadmin/sdk";
const admin = new AdminClient({ apiKey: process.env.VIBEADMIN_API_KEY!, baseUrl: "https://www.easy-admin.solutions" });
await admin.users.upsert({ id: "user_123", email: "ada@example.com", plan: "pro" });
admin.track("workflow.created", { userId: "user_123", properties: { template: "welcome" } });
const stats = await admin.stats();
const draft = await admin.broadcasts.create({ subject: "What's new", html: "<p>Hi {{first_name|there}}</p>", category: "product_updates" });
// a person reviews the recipient count in the dashboard, then:
await admin.broadcasts.send(draft.id, { confirmRecipients: 120 });
await admin.shutdown(); // flushes queued eventsWebhooks
Let your backend push changes instead of calling the API: create a webhook under Webhooks to get a URL and a signing secret, then POST user.created / user.updated / user.deleted / payment events to it. Each request must carry X-VibeAdmin-Signature: t=<unix seconds>,v1=<hex hmac-sha256 of "t.body">; requests older than 5 minutes are rejected.
import { createHmac } from "node:crypto";
const body = JSON.stringify({ type: "user.created", data: { id: "user_123", email: "ada@example.com" } });
const t = Math.floor(Date.now() / 1000);
const v1 = createHmac("sha256", process.env.VIBEADMIN_WEBHOOK_SECRET!).update(`${t}.${body}`).digest("hex");
await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json", "X-VibeAdmin-Signature": `t=${t},v1=${v1}` },
body,
});Connect your own Resend account under Broadcasts → Email settings. Create drafts via the API or MCP, review the audience in the dashboard, and send. Marketing and product-update emails always include an unsubscribe link and skip people who opted out; security and transactional emails do not. Merge tags: {{first_name|there}} {{name}} {{email}} {{plan}} {{meta.field}}.
Limits & errors
600 requests per minute per key (MCP: 300). Bodies up to 2 MB, bulk up to 500 users. Errors look like {"error":{"code":"invalid_request","message":"…"}} with status 400 (invalid), 401 (bad key), 402 (plan limit reached), 403 (missing scope), 404, 409 (conflict, e.g. a user managed by an integration or a changed audience), 413, 429 (rate limit, see Retry-After).