Get Started

Create your API key

How to obtain, rotate, and store the credentials used to authenticate

Octopus Cards uses long-lived API key / API secret pairs to mint short-lived JWTs. The JWTs authenticate individual API requests; the key/secret pair authenticates you to the login endpoint. Two distinct layers, two different lifetimes.

Getting your credentials

API keys are issued per client account by your onboarding contact. For a sandbox account:

  1. Request access through your account manager.
  2. Receive an email with your sandbox API key + API secret.
  3. Store both in your secrets manager (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, etc.) - never in source control, never in a .env.example, never in CI logs.

Production credentials are issued separately after KYC/KYB - see Moving to Production.

Treat the secret like a password

Anyone with your API key + secret can mint access tokens and place orders against your wallets. If either leaks, rotate immediately - contact support for a replacement pair.

How credentials are used

Every authenticated request to the Octopus Cards API requires a JWT access token in the Authorization header:

curl https://api.octopuscards.io/api/v1/products \
  -H "Authorization: Bearer <access_token>"

That access token is obtained by exchanging the long-lived credentials at the login endpoint:

curl -X POST https://api.octopuscards.io/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "your_api_key",
    "password": "your_api_secret"
  }'

The response contains an access token (valid 1 hour) and a refresh token (valid 7 days). Keep using the access token for requests until it expires, then use the refresh token to get a new pair.

Full details: Authentication reference.

Handling credentials in code

The right shape depends on your stack, but two rules are universal:

  1. Load from environment or secrets manager at boot. Never inline them.
  2. Never log them. Redact the username/password fields before any structured logging.

A minimal example:

package client

import "os"

type Credentials struct {
    APIKey    string
    APISecret string
    BaseURL   string
}

func LoadCredentials() Credentials {
    return Credentials{
        APIKey:    os.Getenv("API_KEY"),
        APISecret: os.Getenv("API_SECRET"),
        // e.g. https://sandbox-api.octopuscards.io
        BaseURL:   os.Getenv("BASE_URL"),
    }
}

Rotation

Rotate your API secret in these cases:

  • Suspected leak (unexpected charges, unauthorized login attempts, or a committed .env).
  • Staff departure where the person had access.
  • Scheduled rotation - at least once every 12 months is a sensible default.

Rotation flow:

  1. Request a new secret from your account manager.
  2. Deploy the new secret to your production environment.
  3. Confirm the new secret works by making a login call.
  4. Notify support to revoke the old secret.

Because JWTs are short-lived, you don't need to invalidate any in-flight tokens - they'll expire naturally within the hour.

Your client account can be restricted to a set of CIDR ranges. Requests from any other IP return 403 Forbidden:

{
  "error": {
    "name": "ForbiddenError",
    "code": "FORBIDDEN",
    "message": "IP address not authorized"
  }
}

Share your production egress IPs with your account manager to configure this. See Security for the full threat model.

Sandbox vs production keys

PropertySandboxProduction
Issued whenOn account creationAfter KYC/KYB + funding
ScopeSandbox onlyProduction only
Can they be swapped?No - different hosts will reject mismatched keysNo
Default IP allowlistOffRecommended: on

Keys from one environment will not authenticate against the other. Use separate env vars (API_KEY_SANDBOX, API_KEY_PROD) to avoid mix-ups in code.

Next

On this page