Onsend Docs

Team, API Keys & Webhooks

Invite teammates with the right role, mint API keys for programmatic access, and subscribe your own systems to webhook events.

These three settings pages live under Admin → Settings and govern who can operate your project and how outside systems integrate with it. Team controls human access, API keys control machine access, and Webhooks push your project's events out to your own infrastructure.

Team

Open Settings → Team to see everyone who can access your project's admin surface, their role, and whether they've accepted yet.

Screenshot

Admin → Settings → Team — member list with role and accepted status

slot: team-api-webhooks.team-list

Inviting a teammate

Enter the teammate's email, pick a role, and click Send invite.

Screenshot

Admin → Settings → Team → invite form (email + role)

slot: team-api-webhooks.team-invite

The invitee needs an Onsend account first (V1)

In V1 you can only invite someone who already has an Onsend admin account. If their email has no account yet, the invite returns an error asking them to sign up at /sign-up first, then you retry. (A token-based invite-by-link flow — where the link itself creates the account — is on the roadmap.) Once invited, they appear as pending until the first time they sign in.

The invite emails the teammate a link to the admin dashboard. Email is best-effort: if the send fails, the membership is still created and the teammate can sign in to pick it up.

Roles

Every teammate holds exactly one role. Roles are ranked Owner > Admin > Member > Viewer — you can only assign or modify roles strictly below your own.

RoleCapabilities
OwnerFull control of the project, including team and all settings. There is one Owner (the creator). Owner role can't be assigned, changed, or removed in the dashboard — ownership transfers go through Onsend support.
AdminManages the project end-to-end: campaigns, users, branding, and the Team, API keys and Webhooks pages. Can invite, re-role and remove Members and Viewers (but not other Admins or the Owner).
MemberDay-to-day operator — create and run campaigns, manage users and quests. No access to Team, API keys or Webhooks (those require Admin).
ViewerRead-only across the admin surface. Sees dashboards, campaigns and reports but can't change anything.

Who can manage the team

Inviting, re-roling and removing teammates all require Admin or higher. Members and Viewers can't open these actions. The Team list itself is visible to any role.

Changing a role or removing someone

In the member list, change a teammate's role with the inline role selector, or click Remove to revoke their access immediately. A few guardrails apply:

  • You can't change or remove yourself — a second Admin or the Owner has to do it. This prevents locking the project out of its last operator.
  • You can't touch the Owner — that's a support-only operation.
  • You can only act on teammates strictly below your own role, and you can't promote anyone to your own role or higher.

Every invite, role change and removal is written to the Audit Log.

API keys

API keys let scripts, backend services and CI call Onsend on your project's behalf without a human session. Open Settings → API keys (requires Admin) to mint and manage them.

Screenshot

Admin → Settings → API keys → New API key (name + scopes)

slot: team-api-webhooks.api-keys-mint

Mint a key

Click New API key, give it a recognizable name (e.g. Production worker) and a comma-separated list of scopes (e.g. read:users,read:quests). You can optionally set an expiry; omit it for a non-expiring key.

Copy the raw key once

On creation Onsend shows the full key — formatted onsend_<…>exactly once. Copy it immediately and store it somewhere safe (a secrets manager).

The raw key is shown only once

Onsend stores only a SHA-256 hash of the key plus a short prefix for identification — never the key itself. If you lose it, you can't recover it; revoke the key and mint a new one.

Manage and revoke

Keys appear in the list with their prefix, scopes, and last-used date. Click Revoke to disable a key immediately — any service still using it starts getting 401s. Revoking is a soft-delete (the row is retained for audit) and is idempotent.

Screenshot

Admin → Settings → API keys → revoke confirmation

slot: team-api-webhooks.api-keys-revoke

Webhooks

Webhooks push your project's events to a URL you control, so your own backend can react in real time. Open Settings → Webhooks (requires Admin) to register endpoints.

Register an endpoint

Click New webhook, enter the destination URL and a comma-separated list of events to subscribe to.

Screenshot

Admin → Settings → Webhooks → New webhook (URL + events)

slot: team-api-webhooks.webhook-create

Save the signing secret

On creation Onsend returns an HMAC signing secret (formatted whsec_<…>) once. Copy it now — you'll use it to verify that incoming deliveries genuinely came from Onsend. You can pause (toggle active), edit, or delete a webhook later from the list.

Event types

Events are project-level activity you opt into per endpoint. Common ones:

EventFires when
quest.completedAn end user successfully completes a quest
referral.activatedA referral converts (the referred user qualifies)

Events are free-form

The events field is a comma-separated list, so you can subscribe to any event string your integration expects. New event types are added over time — the full catalog ships with the API reference below.

Verifying the signature

Every delivery is a POST with a JSON body and these headers:

HeaderValue
x-vandergrid-signatureHex-encoded HMAC-SHA256 of the raw request body, keyed with your whsec_ secret
x-vandergrid-event-typeThe event type (e.g. quest.completed)

To verify, compute the HMAC-SHA256 of the raw request body with your secret and compare it (in constant time) to x-vandergrid-signature:

import { createHmac, timingSafeEqual } from "node:crypto";
 
function isValid(rawBody: string, signature: string, secret: string): boolean {
  const expected = createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
  if (expected.length !== signature.length) return false;
  return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
}

Sign over the raw body

Compute the HMAC over the exact bytes Onsend sent, before any JSON re-serialization. Reserializing can reorder keys or change whitespace and break the comparison. Reject any request whose signature doesn't match.

Delivery & retries

A successful delivery is any 2xx response. Onsend does not follow redirects — your endpoint must respond directly. Anything else (a non-2xx, a timeout, a connection error) is treated as a failure and retried with exponential backoff:

AttemptWait before this attempt
1immediate
21s
35s
430s
55min
630min
72hr
86hr

After 8 failed attempts the delivery is marked given up and no longer retried. Make your receiver idempotent — a delivery can legitimately arrive more than once (e.g. when your endpoint 2xx's but the connection drops before Onsend records it).

Health at a glance

The webhook list shows each endpoint's last-delivered time and consecutive failure count, so you can spot a broken endpoint before deliveries are given up on.


This page covers the dashboard surfaces. The full per-endpoint REST reference — every route, request shape, scope and event payload — is documented separately: see the API Reference (coming soon).

On this page