Transactional Email OAuth 2.0 Authentication

Prev Next

OAuth 2.0 is the recommended authentication method for new Transactional Email API v2 integrations. It gives you per-scope permissions, IP allowlisting, short-lived tokens, and credential rotation without touching your send code.

OAuth traffic reaches the API through the Insider Gateway at https://gw.useinsider.com, which handles authentication, routing, and rate limiting before passing the request to the email service. The gateway resolves your account from the token, so no request needs a customer or tenant parameter.

The integration consists of three steps:

  1. Create credentials in the InOne panel and select the scopes you need.

  2. Get an access token by exchanging the Client ID and Secret for a short-lived Bearer token.

  3. Call the API by sending a request to https://gw.useinsider.com/api/em/v2/tx/... with that token.

Only users with Administrator permission can generate credentials. If your account cannot complete this operation, consult the Insider One team.

1. Create a Client ID and Secret

This is a one-time setup.

  1. Navigate to your username > Settings > InOne Settings > Integration Settings.

  2. Scroll to the OAuth 2.0 Credentials section and click the Generate OAuth 2.0 Credential button. Credentials you created earlier are listed here.

  3. Enter a credential name (e.g., my-backend-integration) and select the scopes this credential is allowed to use. A token can only access the scopes granted to its credential.

  4. Set the token duration.

  5. Click the Continue button.

  6. Optionally authorize trusted IP addresses, as single IPs or CIDR ranges, to apply an IP restriction. If you set one, tokens are issued only to and accepted only from those addresses. Click the Authorize and Generate button.

  7. Click Copy to copy the Client ID in ins-{uuid} format, and the Client Secret, a 60-character string, before closing the prompt.

The Client Secret is displayed only once. Store it securely. If it is lost or leaked, revoke the credential and create a new one.

Each user can generate up to 10 tokens per minute. Requests over that limit are blocked and return an error.

Each scope also carries its own rate limit, expressed as requests per window plus a burst allowance, configured when the credential is created. See Rate limiting below.

To edit or delete a credential, use the Edit or Delete button next to it in the same panel section. Editing is limited to the API scope and the IP address authorization.

Scopes

Each endpoint requires its own scope.

Scope

Method

Gateway endpoint

Purpose

em-tx-v2-send

POST

/api/em/v2/tx/send

Sends a transactional email

em-tx-v2-validate

POST

/api/em/v2/tx/validate

Validates template syntax and dry-run render

em-tx-v2-status-get

GET

/api/em/v2/tx/messages/{message_id}

Gets the delivery status of a message

em-tx-v2-categ-create

POST

/api/em/v2/tx/categories

Creates a category

em-tx-v2-categ-list

GET

/api/em/v2/tx/categories

Lists categories

em-tx-v2-categ-get

GET

/api/em/v2/tx/categories/{id}

Gets a single category

2. Get an access token

Exchange your credentials for a JWT access token. Request only the scopes you need, and request every scope you need. The token will contain exactly the scopes in this call, and each must already be granted to the credential.

Request

curl -X POST --location 'https://gw.useinsider.com/auth/token' \
  --header 'Content-Type: application/json' \
  --data '{
    "client_id": "ins-{your_client_id}",
    "client_secret": "{your_client_secret}",
    "scopes": [
      "em-tx-v2-send",
      "em-tx-v2-status-get"
    ]
  }'

Responses

200

{
  "access_token": "{jwt}",
  "token_type": "Bearer",
  "expires_in_sec": 4500
}

401

This response is returned when the Client ID or Secret is wrong, when a requested scope is not granted to the credential, or when the calling IP is not in the allowlist. The response is intentionally identical for all three cases, so check all three when troubleshooting.

Requesting fewer scopes than you use is the most common integration error. A credential may have a scope granted, but each token only carries the scopes explicitly listed in its own token request. Calling an endpoint whose scope was omitted returns R6, not a permissions error on the credential.

Reuse, refresh, and revoke tokens

  • Reuse the token until it expires. The default lifetime is 4500 seconds, that is 75 minutes. Do not request a new token per API call.

  • On a 401 with code R5 (Expired Token), request a new token and retry the call.

  • To invalidate a token before it expires, call POST https://gw.useinsider.com/auth/revoke/token with the Authorization: Bearer header. Subsequent calls with that token return R9 error.

3. Call the API

Every call has the same shape as follows:

curl --location 'https://gw.useinsider.com/api/em/v2/tx/send' \
  --header 'Authorization: Bearer {your_access_token}' \
  --header 'Content-Type: application/json' \
  --data '{
    ...
  }'

Request and response bodies are identical to the API key versions documented on each endpoint page. Only the base URL, the path, and the auth header differ.

Your request must use the method documented for the resource. A GET against a POST-only endpoint returns R6 with HTTP 405, because the path matches a scope but the method does not. A path that matches no scope at all returns R6 with HTTP 401.

Gateway error codes

Errors raised by the gateway itself use a compact envelope:

{
  "code": "",
  "message": ""
}

Code

HTTP

Meaning

What to do

R1

401

Invalid Token: missing, malformed, or bad signature

Check the Authorization: Bearer header.

R3

429

Rate Limited: scope quota exceeded

Back off and retry after the window.

R4

403

IP Restricted: the caller IP is not in the allowlist, or could not be determined

Call from an allowlisted IP, or update the allowlist.

R5

401

Expired Token

Request a new token.

R6

401

Invalid Request: no scope in the token matches the request path

Verify the URL and confirm the scope was requested at token generation.

R6

405

Invalid Request: the path matches a scope, but the method does not

Use the method documented for that resource.

R8

500

Internal Server Error: the gateway failed before reaching the service

Retry; if it persists, contact support.

R9

401

Revoked Token

Request a new token.

R10

401

Inactive Client: the credential is disabled

Contact support.

A gateway error means the request never reached the email service, so nothing was queued or sent.

Service-level errors, meaning validation failures, not-found responses, and conflicts, use a different envelope and are documented in Error Codes.

Rate limit

Gateway rate limits are enforced per credential and per scope, and the token carries the limits. Defaults are set when the credential is created; the typical default is 100 requests per 60 seconds with a burst allowance of 10.

On a breach, the gateway returns the following with HTTP 429.

{
  "code": "R3",
  "message": "Rate Limited"
}

No rate-limit response headers exist; you can rely on the JSON body.

This limit is separate from and evaluated before the service-level send limit of 10,000 requests per second per customer described in Transactional Emails.

Quick start

# 1. Create a credential in the panel
# (username > Settings > InOne Settings > Integration Settings > OAuth 2.0 Credentials)
# and select the scopes you need.

# 2. Get a token, valid for 75 minutes
TOKEN=$(curl -s https://gw.useinsider.com/auth/token \
  -H 'Content-Type: application/json' \
  -d '{"client_id":"ins-...","client_secret":"...","scopes":["em-tx-v2-send","em-tx-v2-status-get"]}' \
  | jq -r .access_token)

# 3. Send
curl https://gw.useinsider.com/api/em/v2/tx/send \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "subject":"Hi {{ name }}",
    "from":{"email":"orders@yourbrand.com"},
    "recipients":[{"email":"jane@example.com","fields":{"name":"Jane"}}],
    "content":[{"type":"text/html","value":"<p>Hello {{ name }}</p>"}]
  }'

# 4. Track
curl https://gw.useinsider.com/api/em/v2/tx/messages/{message_id} \
  -H "Authorization: Bearer $TOKEN"