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

Errors

HTTP status codes the Insy REST API returns, the two failure body shapes, and the usual causes behind 400, 401, 403, 404 and 409 responses.

Failures come back in one of two body shapes, depending on where in the request the failure happened. Branch on the HTTP status first, then read whichever body you got. success is present in both.

Rejected before the endpoint runs

A key that fails authentication, a body the validator refuses, a permission bit or community role that says no — these are raised before the endpoint’s own code runs and are serialised by the global exception filter:

{
  "success": false,
  "message": "Unauthorized",
  "statusCode": 401,
  "timestamp": "2026-09-01T10:15:30.000Z",
  "path": "/membership/provision-external"
}

There is no errorCode and no data key at all. This is the shape behind almost every 400, 401 and 403.

Returned by the endpoint

Failures the endpoint decides on itself come back in the BaseResponse envelope:

{
  "success": false,
  "errorCode": "not_found",
  "message": "Community not found",
  "correlationKey": "0e9a1c47f2b84d6a",
  "logUrl": "https://observability.insy.io/explore?..."
}
Field Type Description
success boolean Always false on failure.
errorCode string A short machine-readable code, for example not_found.
message string A human-readable explanation, safe to log.
correlationKey string The trace ID for the request. Quote it when you report a problem.
logUrl string An Insy-internal log link for the same trace. You cannot open it; pass it on unchanged.

data is omitted rather than sent as null. correlationKey and logUrl are present only when the request carried a trace ID, so treat both as optional.

Do not show message to end users verbatim. It is written for developers and can name internal concepts.

Status codes

Status Meaning What to do
400 Validation failed. A required field is missing, malformed, or outside its allowed values. Fix the request. Retrying the same body will fail again.
401 The API key is missing, malformed, unknown or revoked. Check the Authorization header and the key itself. Do not retry.
403 Authenticated, but not allowed. The permission bit is missing, or the key owner is not OWNER or ADMIN of the community. Issue a new key with that permission at Account → Developer → API keys — permissions cannot be edited after creation — or point the call at a community the owner administers. Do not retry.
404 The addressed resource does not exist — a community or reservation that is not there, or a checkout whose payment has not completed yet. Verify the IDs. Do not retry.
409 The membership already exists. onOverlap: "error" hit an active membership, or the membership is managed by a Stripe subscription and cannot be provisioned externally. Also returned when the same address is signed up twice for one drop. Send extend or replace, or leave the Stripe subscription alone. Do not retry.
500 Something failed on Insy’s side. Retry with backoff. If it persists, contact Insy with the correlationKey, the message and the time of the call.

The same mapping can also produce 412, 422, 503 and 504. If you meet a status that is not in the table, treat it by its class: 4xx means change the request, 5xx means retry with backoff. API-key requests are not rate limited, so there is no 429 or retry-after to handle — but avoid tight polling anyway.

401 versus 403

  • 401 normally means Insy could not identify you. The key is absent, mistyped, or no longer active. That body carries statusCode and has no errorCode.
  • 403 means Insy knows who you are and is refusing. Either the key lacks the permission bit for that endpoint, or the key’s owner is not an OWNER or ADMIN of the communityId in the request.

A key that works against one community and returns 403 against another is almost always the second case.

One endpoint breaks the 401 rule. GET /membership/join-telegram-community-external/{externalId} answers 401 with "errorCode": "unauthorized" and the message “You are not a member of this community” when the buyer behind that checkout has no active membership. The key is fine; the membership is not. Tell the two apart by the presence of errorCode — a rejected key never has one.

OAuth errors are shaped differently

The OAuth token, revoke, userinfo, memberships and products endpoints do not use the BaseResponse envelope. They return bare RFC-shaped JSON.

{
  "error": "invalid_grant",
  "error_description": "..."
}

invalid_client is returned with HTTP 401; every other token error is 400. Resource endpoints return 401 with "error": "invalid_token" and 403 with "error": "insufficient_scope". No WWW-Authenticate response header is sent, so a client library that waits for one to decide how to re-authenticate will not get the hint. See Sign in with Insy.

Common causes

401 on every request, but the key looks rightHeader formatting

The key goes in Authorization as a bearer token: Authorization: Bearer insy_.... There is no x-api-key header. Check that the word Bearer and a single space precede the key, that nothing has trimmed the insy_ prefix, and that no newline was copied along with it. If the header is correct, the key may have been revoked — revocation takes effect immediately.

403 even though the key has the right permissionCommunity role

Whenever a request carries a communityId, the key’s owner must additionally hold an active OWNER or ADMIN membership in that community. The permission bit and the community role are two independent checks, and failing the second produces 403. Confirm the communityId is the one the key was issued for, and that the owner’s membership in it is still active.

400 on provision-externalIdentifying the user

Provisioning needs a way to find or create the person: supply email or telegramUserId. Also check that communityId and pricingPlanId are UUIDs, that validFrom and validUntil are ISO-8601 timestamps, and that onOverlap is one of extend, replace or error. For a lifetime membership, omit validUntil — do not send null.

The same buyer was provisioned twiceIdempotency key

externalOrderId is the idempotency key, and it only protects you if it is stable. A value derived from a timestamp, a UUID generated at call time, or a retry counter is a different key on every attempt, so each attempt provisions. Derive it from something durable on your side, such as your order ID. The check is also scoped: Insy compares the ID against the buyer’s current membership row for that community only, so replaying an old order after a later one for the same buyer, or sending the same ID with a different email or telegramUserId, provisions again. See Idempotency.

buy-plan did not return a checkoutUrlRequired fields

pricingPlanId, communityId, externalId, email, successUrl and cancelUrl are all required. successUrl and cancelUrl must be valid URLs, externalId is your own identifier for the buyer rather than an Insy ID, and utmSource is capped at 100 characters. A missing email and an unknown pricingPlanId both come back as 201 with no checkoutUrl, so read message to tell them apart.

404 for an ID that exists in your dashboardWrong ID or a payment still in flight

Ownership is enforced through the communityId in the request, so an endpoint that does not carry one — GET /membership/join-telegram-community-external/{externalId} — is not scoped to your communities. A 404 there means the externalId is unknown, not that it belongs to someone else: check you sent the externalId from buy-plan and not an externalOrderId from provision-external, which creates no checkout reservation at all. The same endpoint returns 404 with “Membership not found, membership has not been processed yet” while the buyer’s payment is still in flight — wait for the payment.success webhook and call again.

Reporting a problem

There is no per-key usage log, so Insy cannot look up your request after the fact from the key alone. When you report an issue, include the endpoint, the UTC timestamp, the HTTP status, and the errorCode and message from the response body.

If the body carried a correlationKey, quote it. That is the trace ID Insy can look the request up by directly, and it turns a report into a single query. Send the logUrl alongside it if one was present.

Was this page helpful?