Skip to content
Insy
For developers
Esc
navigateopen⌘Jpreview
On this page

API keys

How Insy API keys look, how to get one, how to send them, and how the permission bitmask and community-role check decide what a key may do.

Server-to-server calls to the Insy REST API authenticate with an API key. A key carries a fixed set of permissions and belongs to a person, so what it can do is the intersection of its permission bits and that person’s role in the community you name.

What a key looks like

A key is the prefix insy_ followed by 32 lowercase hexadecimal characters — 16 random bytes rendered as hex.

insy_4f3a9c1e7b2d5081a6c4e9f3b7d2508a

Insy stores only an unsalted SHA-256 digest of the key. The plaintext is displayed exactly once, at creation, and cannot be recovered afterwards. Copy it into your secret store the moment you receive it; if you lose it, the only remedy is a new key.

Getting a key

You issue your own keys from Account → Developer → API keys — create one, pick its permissions, pin it to your servers’ IPs, rotate its secret and revoke it, without asking anyone.

Create the key

Open API keys and press Create key. Give it a name you will recognise in six months — the name is all you see in the list, since the secret never appears again.

Pick the permissions

Tick the ones from the table below your integration actually needs rather than all of them. MEMBERSHIP_WRITE covers the external join-Telegram funnel; add CHECKOUT_WRITE only if you also open hosted checkout sessions. The privileged permission is not offered on this form at all — see Privileged permissions.

Restrict it to your servers

Optional, and worth doing: list the public egress IPs your backend calls from, so a leaked key is useless anywhere else. See IP allowlist.

Store the plaintext

The key is shown once, when you press create. Put it in your secret manager or environment before you close the dialog.

A key belongs to your account, not to one community: it reaches every community where that account holds an active OWNER or ADMIN membership, and no others. See Community role below.

Sending a key

Put the key in the Authorization header as a bearer token.

Authorization: Bearer insy_4f3a9c1e7b2d5081a6c4e9f3b7d2508a

There is no x-api-key header. A request without the Authorization header, or with a key that is unknown or revoked, is rejected with 401.

curl -X POST https://api.insy.io/membership/provision-external \
  -H "Authorization: Bearer insy_4f3a9c1e7b2d5081a6c4e9f3b7d2508a" \
  -H "Content-Type: application/json" \
  -d '{
    "communityId": "3b7d1f42-9c8a-4e51-b0d6-2a4f8e7c1953",
    "externalOrderId": "order_10241",
    "email": "buyer@example.com"
  }'
// Built-in fetch — Node 18 or newer, no dependencies.
const response = await fetch("https://api.insy.io/membership/provision-external", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    communityId: "3b7d1f42-9c8a-4e51-b0d6-2a4f8e7c1953",
    externalOrderId: "order_10241",
    email: "buyer@example.com",
  }),
});

const body = await response.json();

if (!response.ok || !body.success) {
  throw new Error(`${body.errorCode ?? response.status}: ${body.message ?? "request failed"}`);
}

console.log(body.data);

Permissions

Permissions are a bitmask. Each permission is a power of two, and a key stores the bitwise OR of everything it was granted. Every bit has a readable scope string — the same grant in a friendlier form.

Permission Value Scope string Unlocks
MEMBERSHIP_READ 1 membership.read GET /membership/check-telegram/{communityId}/{telegramUserId}
MEMBERSHIP_WRITE 2 membership.write GET /membership/join-telegram-community-external/{externalId}
CHECKOUT_WRITE 4 checkout.write POST /api/checkout/buy-plan
DROP_NOTIFICATION_WRITE 8 drop_notification.write POST /api/drop-notification/signup
MEMBERSHIP_PROVISION 16 membership.provision POST /membership/provision-externalprivate, see below

So a key with value 3 holds MEMBERSHIP_READ and MEMBERSHIP_WRITE; a key with all four standard permissions stores 15. Ask for the smallest set that covers your integration — a key you issue for a checkout page has no reason to also write memberships.

Privileged permissions

MEMBERSHIP_PROVISION (value 16) is a privileged permission. It unlocks POST /membership/provision-external, which mints paid community access without a payment, so it is granted only to vetted partners — request it from support@insy.io.

It sits deliberately outside the standard set. provision-external requires MEMBERSHIP_WRITE and MEMBERSHIP_PROVISION together, so a broad key that holds every standard permission (15) still gets a 403 there until the provision bit is added on top. This is intentional: provisioning is never unlocked by breadth alone, only by an explicit, separately-granted permission.

Community role is checked separately

Whenever a request carries a communityId — in the body or in the path — the permission bit is not enough on its own. The person who owns the key must also hold an active OWNER or ADMIN membership in that community.

If the bit is set but the role is not, the request fails with 403. This is the most common surprise when a key that works against one community is pointed at another.

Lifetime and revocation

Keys you create yourself have no expiry date, and there is no rotation deadline — a key keeps working until you rotate or revoke it. (The backend does honour an expiry when one is set, but the self-serve form does not offer the field.)

Revocation is a soft disable: the key is marked inactive and stops authenticating immediately, on the next request. Nothing it created is rolled back, and memberships it provisioned stay in place.

Rotation

Press Rotate on a key in API keys. Rotation issues a new secret for the same key — its name, permissions and IP allowlist are unchanged — and the plaintext is shown once, exactly like at creation. The old secret stops working the moment you rotate, so deploy the new one to a place it can be swapped in quickly, then rotate.

The list shows a Last used timestamp per key, so you can tell a live key from a forgotten one before you retire it. It is written at most once a minute, so treat it as “used recently”, not as a call counter — there is no per-request log.

IP allowlist

A key can be pinned to a set of source addresses. Leave the allowlist empty and the key works from anywhere; add entries and every other source address is rejected with 401.

Each entry is an exact IP or an IPv4 CIDR range:

203.0.113.7
203.0.113.0/24

The source address is taken from the proxy chain in front of the API (the same one the rate limiter trusts), so put your server’s public egress IP here — not a private 10.x/192.168.x address, which never appears as the source. If a restricted key stops working after an infrastructure change, its egress IP has almost certainly moved.

Security

  • Server-side only. Call the API from your backend and expose your own endpoint to your front end.
  • Keep it out of source control. Read it from the environment or a secret manager. Never commit it, and never paste it into a support ticket or an issue.
  • Keep it out of URLs. Send it in the Authorization header only, never as a query parameter, so it does not end up in logs you do not control.
  • Scope it down. Request only the permission bits your integration needs.
  • Rotate on suspicion. If a key may have leaked, rotate it yourself — the old secret dies immediately, and the replacement keeps the same permissions and IP allowlist.

Operational notes

  • Requests authenticated with an API key bypass the platform’s request-signature requirement, so there is nothing extra to compute or sign.
  • API-key requests are not rate limited by the application. Be a good citizen anyway: retry with backoff rather than hammering, and use the idempotency contract described in Endpoints so a retry cannot double-provision.
  • The only usage signal is the Last used timestamp, throttled to one write a minute. There is no per-request log, so keep your own record of the calls you make if you need an audit trail.

Was this page helpful?