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
Invite teammates and assign one of four roles — from read-only Viewer up to Owner.
API keys
Mint per-project keys for scripts, services and CI to call Onsend programmatically.
Webhooks
Subscribe your backend to events like quest.completed and verify deliveries with HMAC.
Team
Open Settings → Team to see everyone who can access your project's admin surface, their role, and whether they've accepted yet.
Admin → Settings → Team — member list with role and accepted status
slot: team-api-webhooks.team-listInviting a teammate
Enter the teammate's email, pick a role, and click Send invite.
Admin → Settings → Team → invite form (email + role)
slot: team-api-webhooks.team-inviteThe 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.
| Role | Capabilities |
|---|---|
| Owner | Full 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. |
| Admin | Manages 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). |
| Member | Day-to-day operator — create and run campaigns, manage users and quests. No access to Team, API keys or Webhooks (those require Admin). |
| Viewer | Read-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.
Admin → Settings → API keys → New API key (name + scopes)
slot: team-api-webhooks.api-keys-mintMint 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.
Admin → Settings → API keys → revoke confirmation
slot: team-api-webhooks.api-keys-revokeWebhooks
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.
Admin → Settings → Webhooks → New webhook (URL + events)
slot: team-api-webhooks.webhook-createSave 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:
| Event | Fires when |
|---|---|
quest.completed | An end user successfully completes a quest |
referral.activated | A 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:
| Header | Value |
|---|---|
x-vandergrid-signature | Hex-encoded HMAC-SHA256 of the raw request body, keyed with your whsec_ secret |
x-vandergrid-event-type | The 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:
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:
| Attempt | Wait before this attempt |
|---|---|
| 1 | immediate |
| 2 | 1s |
| 3 | 5s |
| 4 | 30s |
| 5 | 5min |
| 6 | 30min |
| 7 | 2hr |
| 8 | 6hr |
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).