---
title: Quickstart
description: Choose between the API key, OAuth and webhook integration surfaces, then make your first authenticated call to the Insy API.
---

Insy exposes three integration surfaces, and which one you want depends on whose data you are
touching. Pick a surface, then follow the section that matches.

- **API key** — server-to-server. You act on behalf of a community you own or administer.
- **Sign in with Insy (OAuth 2.0)** — you act on behalf of an Insy user, after they grant you
  access.
- **Webhooks** — you react to things that happen inside Insy, with no polling.

They are not mutually exclusive. A typical integration uses an API key to create checkouts and a
webhook to learn when the payment succeeded.

<CardGroup cols={2}>
  <Card title="I run the community" href="/developers/api" icon="key">
    You have your own funnel, landing page or CRM and want to provision memberships and open
    checkouts. Use an API key.
  </Card>
  <Card title="I want users to log in" href="/developers/oauth" icon="shield-check">
    You are building a product for Insy users and need to read their memberships or products with
    their consent. Use OAuth.
  </Card>
  <Card title="I need to know when things happen" href="/developers/webhooks" icon="plug">
    Payments, membership changes and digital product purchases, pushed to your endpoint as signed
    JSON.
  </Card>
  <Card title="I just want the reference" href="/developers/api/endpoints" icon="list">
    Every endpoint an API key can call, with request and response shapes.
  </Card>
</CardGroup>

## Choosing a surface

|                        | API key                                       | OAuth 2.0                                | Webhooks                                     |
| ---------------------- | --------------------------------------------- | ---------------------------------------- | -------------------------------------------- |
| Direction              | You call Insy                                 | You call Insy                            | Insy calls you                                |
| Acts on behalf of      | Communities you administer                    | An Insy user who consented               | Not applicable                                |
| Credential             | `insy_` key, `Authorization: Bearer`          | `client_id` and access token             | A 64-hex signing secret                       |
| Who issues it          | You, inside the Insy app                      | You, inside the Insy app                 | The creator, inside the Insy app              |
| User interaction       | None                                          | The user sees a consent screen           | None                                          |
| Scope of access        | Permission bitmask, plus OWNER or ADMIN on the community | `memberships.read`, `products.read` | All five event types, filter in your handler  |
| Typical use            | Provision a membership, create a checkout     | Sign in, read the user's memberships     | Fulfil an order when payment succeeds         |
| Runs in a browser      | No — the key is a server secret               | Yes, via the login widget                | No                                            |

> **Never ship an API key to a browser**
>
> An API key acts as the user it was issued to, on every community where that user is an OWNER or
> ADMIN — not just one. It belongs on your server, in an environment variable. If you need
> something client-side, use the OAuth login widget, which is a public client and holds no secret.

## Path 1 — API key

Use this when you own the community and want to drive it from your own systems.

1. **Create a key**

    Open [Account → Developer → API keys](https://insy.io/account/developer/api-keys), press
    **Create key**, and tick the permissions you need: `MEMBERSHIP_READ`, `MEMBERSHIP_WRITE`,
    `CHECKOUT_WRITE`, `DROP_NOTIFICATION_WRITE`. A key belongs to a user account rather than to
    one community, so it reaches every community that account administers. The plaintext key is
    shown once, at creation, and stored only as a hash — save it immediately.

2. **Confirm your community role**

    Every request that carries a `communityId` also checks that the key owner holds an active
    OWNER or ADMIN membership in that community. Without it the call fails with 403 even when the
    permission bit is set.

3. **Make a call**

    Send the key as `Authorization: Bearer insy_...`. There is no `x-api-key` header. Full details
    in [Authentication](/developers/api/authentication) and the
    [endpoint reference](/developers/api/endpoints).

### Your first request

This creates a hosted checkout session and returns a URL you send the buyer to. It needs the
`CHECKOUT_WRITE` permission.

<CodeGroup>

```bash title="curl"
curl -X POST https://api.insy.io/api/checkout/buy-plan \
  -H "Authorization: Bearer insy_4f3a9c1e7b2d5081a6c4e9f3b7d2508a" \
  -H "Content-Type: application/json" \
  -d '{
    "pricingPlanId": "8f14e45f-ceea-4d19-9b0a-1c2d3e4f5a6b",
    "communityId": "3c9a7b21-5d4e-4f8a-9c1b-0e2d4f6a8b3c",
    "externalId": "order_10482",
    "email": "buyer@example.com",
    "successUrl": "https://shop.example/thanks",
    "cancelUrl": "https://shop.example/pricing",
    "utmSource": "newsletter"
  }'
```

```js title="Node.js"
const response = await fetch("https://api.insy.io/api/checkout/buy-plan", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    pricingPlanId: "8f14e45f-ceea-4d19-9b0a-1c2d3e4f5a6b",
    communityId: "3c9a7b21-5d4e-4f8a-9c1b-0e2d4f6a8b3c",
    externalId: "order_10482",
    email: "buyer@example.com",
    successUrl: "https://shop.example/thanks",
    cancelUrl: "https://shop.example/pricing",
    utmSource: "newsletter",
  }),
});

const body = await response.json();

// buy-plan answers 201 even when it fails, so a missing URL means failure.
const url = body?.data?.checkoutUrl;
if (!url) {
  throw new Error(body?.message ?? "checkout session was not created");
}
console.log(url);
```

</CodeGroup>

A successful response uses the standard envelope:

```json title="Response"
{
  "success": true,
  "data": { "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_live_..." }
}
```

`errorCode` and `message` appear only on failures, so branch on `success`, not on
`errorCode === null`. buy-plan returns `201` even when it fails — treat a missing
`data.checkoutUrl` as failure and read `message` for the reason.

The host of `checkoutUrl` depends on the payment provider configured on the plan: a Stripe-hosted
page, or CoinGate for a crypto plan. Treat `checkoutUrl` as opaque, redirect the buyer to it
as-is, and do not allowlist a host.

> **This is not a sandbox**
>
> Insy has one environment. The request above creates a real checkout session and completing it
> takes a real payment — there is no test mode. Point your first calls at a community you control.

## Path 2 — Sign in with Insy

Use this when the data belongs to an Insy user rather than to you.

1. **Register a client**

    Open [Account → Developer → OAuth clients](https://insy.io/account/developer/oauth-clients)
    and press **Create client**: application name, redirect URIs, the scopes you need
    (`memberships.read`, `products.read`), and — for a browser widget — the web origins it will
    run on. You get a `client_id` back straight away, plus a `client_secret` if the client is
    confidential.

2. **Pick confidential or public**

    A confidential client has a secret and sends it in the token request body. A public client has
    no secret and must use PKCE. The type is a property of the client, not something chosen per
    request, and you can change it later on the client's own screen. See
    [PKCE](/developers/oauth/pkce).

3. **Run the flow**

    Either drop in the [login widget](/developers/oauth/widget) — one script tag, no backend — or implement
    the [server-side authorization code flow](/developers/oauth/server-side) yourself. Then call
    [the resource endpoints](/developers/oauth/reference) with the access token.

> **Two things that break strict OAuth libraries**
>
> `POST /oauth/token` answers with HTTP 201, not the 200 the RFC mandates, and HTTP Basic
> (`client_secret_basic`) is not supported — credentials go in the request body
> (`client_secret_post`). Configure your library accordingly. Both are covered in
> [the OAuth reference](/developers/oauth/reference).

## Path 3 — Webhooks

Use this to learn about payments, membership changes and digital product purchases without
polling.

1. **Register an endpoint**

    Webhooks are registered inside the Insy app by the account that owns the community or product,
    not through a public API. You receive a signing secret of 64 hex characters, shown when the
    webhook is created and returned again when you re-save it.

2. **Verify every request**

    `X-Insy-Signature` is an HMAC-SHA256 of the raw request body, keyed with the secret as a
    literal 64-character string — do not hex-decode it first. Verify before parsing the JSON. See
    [Verifying signatures](/developers/webhooks/verify).

3. **Handle the five event types**

    Every active webhook receives all of `membership.created`, `membership.updated`,
    `payment.success`, `payment.failed` and `digital.product.purchase`; there is no per-event
    subscription. Switch on `X-Insy-Event-Type`, deduplicate on `X-Insy-Event-Id`, and respond 2xx
    within 10 seconds. See [Events](/developers/webhooks/events).

## Next

<CardGroup cols={2}>
  <Card title="API authentication" href="/developers/api/authentication" icon="lock">
    Key format, the permission bitmask and the community role check.
  </Card>
  <Card title="Endpoint reference" href="/developers/api/endpoints" icon="terminal">
    The complete list of five endpoints an API key can call.
  </Card>
  <Card title="Webhook events" href="/developers/webhooks/events" icon="webhook">
    The envelope and the payload fields of each event type.
  </Card>
  <Card title="OAuth reference" href="/developers/oauth/reference" icon="code">
    Endpoints, token lifetimes, error shapes and known deviations from the RFC.
  </Card>
</CardGroup>
