# Sandbox and production URLs

Use these values to switch between Sandbox and Production without code changes. Keep your base URLs, realms, credentials, and environment-specific onboarding values in configuration, then load the correct set per environment.

Bead uses different authentication methods depending on the API family:

* Payments APIs use an API key sent as `X-Api-Key`
* Onboarding APIs use an API key sent as `X-Api-Key`
* Other Bead APIs may use OAuth 2.0 access tokens depending on the product area and endpoint family

### Sandbox values

#### Payments

* Payments API base URL: `https://api.test.devs.beadpay.io`

Auth header:

```http
X-Api-Key: {apiKey}
```

#### Onboarding

Onboarding requests in Sandbox use your Sandbox onboarding API key.

Auth header:

```http
X-Api-Key: {apiKey}
Content-Type: application/json
Accept: application/json
```

#### Onboarding environment rule

When creating onboarding applications in Sandbox, set `cryptoEnvironment` to `sandbox`.

This applies to both onboarding create flows:

* `POST /merchant-onboarding/applications-short`
* `POST /merchant-onboarding/applications`

For the minimal flow, `cryptoEnvironment` is a top-level field.

```json
{
  "merchantName": "Northwind Coffee Roasters",
  "partnerId": "690e2a43852fff2eff800c83",
  "partnerMid": "northwind",
  "signerName": "Morgan Reed",
  "signerEmail": "morgan.reed@example.com",
  "feeInformation": {
    "achSettlementFixedFee": {
      "sellRate": 0.79,
      "isBilledByPartner": false
    },
    "wireSettlementFixedFee": {
      "sellRate": 20,
      "isBilledByPartner": false
    },
    "settlementReturnFixedFee": {
      "sellRate": 25,
      "isBilledByPartner": false
    },
    "monthlyMaintenanceFee": {
      "sellRate": 19.99,
      "isBilledByPartner": false
    }
  },
  "cryptoEnvironment": "sandbox"
}
```

For the full flow, `cryptoEnvironment` is inside `merchantData`.

{% code expandable="true" %}

```json
{
  "signer": {
    "name": "Morgan Reed",
    "email": "morgan.reed@example.com",
    "position": "Owner"
  },
  "merchantData": {
    "merchantName": "Northwind Coffee Roasters",
    "partnerId": "690e2a43852fff2eff800c83",
    "partnerExternalId": "northwind-ext-001",
    "registeredName": "Northwind Coffee Roasters LLC",
    "isSettlementOnly": false,
    "isCommissionMerchant": false,
    "federalTaxId": "123456789",
    "naicsCode": "722515",
    "merchantCategoryCode": "5812",
    "businessDescription": "Retail coffee roaster and cafe",
    "registeredLegalAddress": {
      "address1": "123 Main Street",
      "address2": null,
      "city": "Boston",
      "region": "MA",
      "country": "US",
      "postalCode": "02110"
    },
    "isLegalAddress": true,
    "isOperatingAddress": true,
    "isVirtualAddress": false,
    "locationName": "Northwind Coffee Roasters - Main",
    "businessWebsite": "https://northwind.example.com",
    "businessEmail": "ops@northwind.example.com",
    "businessPhone": "+16175550100",
    "mainPointOfContact": {
      "firstName": "Morgan",
      "lastName": "Reed",
      "email": "morgan.reed@example.com",
      "phoneNumber": "+16175550100"
    },
    "hasNoOwners": false,
    "stakeholders": [],
    "bankName": "First Example Bank",
    "routingNumber": "021000021",
    "accountNumber": "123456789012",
    "bankAddress": {
      "address1": "100 Bank Plaza",
      "address2": null,
      "city": "Boston",
      "region": "MA",
      "country": "US",
      "postalCode": "02110"
    },
    "feeInformation": {
      "achSettlementFixedFee": {
        "sellRate": 0.79,
        "isBilledByPartner": false
      },
      "wireSettlementFixedFee": {
        "sellRate": 20,
        "isBilledByPartner": false
      },
      "settlementReturnFixedFee": {
        "sellRate": 25,
        "isBilledByPartner": false
      },
      "monthlyMaintenanceFee": {
        "sellRate": 19.99,
        "isBilledByPartner": false
      }
    },
    "cryptoEnvironment": "sandbox"
  }
}
```

{% endcode %}

#### OAuth token URL

Use this for OAuth APIs and legacy integrations.

* OIDC token URL: `https://identity.beadpay.io/realms/nonprod/protocol/openid-connect/token`
* Realm: `nonprod`

### Production values

#### Payments

* Payments API base URL: `https://api.devs.beadpay.io`

Auth header:

```http
X-Api-Key: {apiKey}
```

#### Onboarding

Onboarding requests in Production use your Production onboarding API key.

Auth header:

```http
X-Api-Key: {apiKey}
Content-Type: application/json
Accept: application/json
```

#### Onboarding environment rule

When creating onboarding applications in Production, set `cryptoEnvironment` to `production`.

For the minimal flow:

```json
{
  "merchantName": "Northwind Coffee Roasters",
  "partnerId": "690e2a43852fff2eff800c83",
  "partnerMid": "northwind",
  "signerName": "Morgan Reed",
  "signerEmail": "morgan.reed@example.com",
  "feeInformation": {
    "achSettlementFixedFee": {
      "sellRate": 0.79,
      "isBilledByPartner": false
    },
    "wireSettlementFixedFee": {
      "sellRate": 20,
      "isBilledByPartner": false
    },
    "settlementReturnFixedFee": {
      "sellRate": 25,
      "isBilledByPartner": false
    },
    "monthlyMaintenanceFee": {
      "sellRate": 19.99,
      "isBilledByPartner": false
    }
  },
  "cryptoEnvironment": "production"
}
```

For the full flow:

{% code expandable="true" %}

```json
{
  "signer": {
    "name": "Morgan Reed",
    "email": "morgan.reed@example.com",
    "position": "Owner"
  },
  "merchantData": {
    "merchantName": "Northwind Coffee Roasters",
    "partnerId": "690e2a43852fff2eff800c83",
    "partnerExternalId": "northwind-ext-001",
    "registeredName": "Northwind Coffee Roasters LLC",
    "isSettlementOnly": false,
    "isCommissionMerchant": false,
    "federalTaxId": "123456789",
    "naicsCode": "722515",
    "merchantCategoryCode": "5812",
    "businessDescription": "Retail coffee roaster and cafe",
    "registeredLegalAddress": {
      "address1": "123 Main Street",
      "address2": null,
      "city": "Boston",
      "region": "MA",
      "country": "US",
      "postalCode": "02110"
    },
    "isLegalAddress": true,
    "isOperatingAddress": true,
    "isVirtualAddress": false,
    "locationName": "Northwind Coffee Roasters - Main",
    "businessWebsite": "https://northwind.example.com",
    "businessEmail": "ops@northwind.example.com",
    "businessPhone": "+16175550100",
    "mainPointOfContact": {
      "firstName": "Morgan",
      "lastName": "Reed",
      "email": "morgan.reed@example.com",
      "phoneNumber": "+16175550100"
    },
    "hasNoOwners": false,
    "stakeholders": [],
    "bankName": "First Example Bank",
    "routingNumber": "021000021",
    "accountNumber": "123456789012",
    "bankAddress": {
      "address1": "100 Bank Plaza",
      "address2": null,
      "city": "Boston",
      "region": "MA",
      "country": "US",
      "postalCode": "02110"
    },
    "feeInformation": {
      "achSettlementFixedFee": {
        "sellRate": 0.79,
        "isBilledByPartner": false
      },
      "wireSettlementFixedFee": {
        "sellRate": 20,
        "isBilledByPartner": false
      },
      "settlementReturnFixedFee": {
        "sellRate": 25,
        "isBilledByPartner": false
      },
      "monthlyMaintenanceFee": {
        "sellRate": 19.99,
        "isBilledByPartner": false
      }
    },
    "cryptoEnvironment": "production"
  }
}
```

{% endcode %}

#### OAuth token URL

Use this for OAuth APIs and legacy integrations.

* OIDC token URL: `https://identity.beadpay.io/realms/prod/protocol/openid-connect/token`
* Realm: `prod`

Always use Production credentials and Production IDs in Production.

### Recommended configuration approach

Keep these values in environment variables or secrets, and select them at runtime based on your deployment environment.

Example configuration:

* `BEAD_PAYMENTS_BASE_URL`
* `BEAD_IDENTITY_TOKEN_URL`
* `BEAD_REALM`
* `BEAD_TERMINAL_API_KEY`
* `BEAD_ONBOARDING_API_KEY`
* `BEAD_ONBOARDING_CRYPTO_ENVIRONMENT`
* `BEAD_MERCHANT_ID`
* `BEAD_TERMINAL_ID`

Recommended values:

* Sandbox: `BEAD_ONBOARDING_CRYPTO_ENVIRONMENT=sandbox`
* Production: `BEAD_ONBOARDING_CRYPTO_ENVIRONMENT=production`

### Production switch checklist

#### If you are using Payments

1. Switch the Payments base URL from Sandbox to Production.
2. Use your Production API key, `merchantId`, and `terminalId`.
3. Keep the same Payments request headers.

#### If you are using onboarding APIs

1. Switch your onboarding credentials to the Production values for that environment.
2. Use your Production onboarding API key.
3. Keep the same onboarding request header.
4. Change `cryptoEnvironment` from `sandbox` to `production` in onboarding create requests.

#### If you are using OAuth APIs

1. Switch the realm from `nonprod` to `prod`.
2. Switch the token URL to the Production token URL.
3. Use your Production OAuth client credentials and user credentials.
4. Keep the same OAuth request headers.

### Verify you are in the right environment

Use these checks when you see unexpected `401`, `403`, or onboarding validation errors right after switching environments.

#### What to check

* Payments base URL is correct
  * Sandbox: `api.test.devs.beadpay.io`
  * Production: `api.devs.beadpay.io`
* Token URL realm is correct for OAuth APIs
  * Sandbox: `/realms/nonprod/`
  * Production: `/realms/prod/`
* Credentials match the environment
  * Sandbox API keys and IDs only work in Sandbox
  * Production API keys and IDs only work in Production
* Onboarding `cryptoEnvironment` matches the environment
  * Sandbox onboarding requests must use `sandbox`
  * Production onboarding requests must use `production`
* Do not mix environment identifiers or field values
  * A Sandbox onboarding API key with `"cryptoEnvironment": "production"` is misconfigured
  * A Production onboarding API key with `"cryptoEnvironment": "sandbox"` is misconfigured

### Quick interpretation of common errors

* `401 Unauthorized` for Payments usually means the API key is missing, invalid, or the header name is wrong. The header must be exactly `X-Api-Key`.
* `401 Unauthorized` for OAuth APIs usually means the access token is missing, expired, or the realm and credentials are mismatched.
* `403 Forbidden` for Payments usually means the request is authenticated, but the API key is not permitted for the `merchantId` or `terminalId` you sent, or you are mixing environments.
* `403 Forbidden` for Onboarding usually means the onboarding API key is valid, but it does not have permission for the requested action or environment.
* `400 Bad Request` for onboarding create requests should prompt you to check whether `cryptoEnvironment` is missing, misplaced, or set to the wrong enum value.
  * For `POST /merchant-onboarding/applications-short`, `cryptoEnvironment` must be top-level.
  * For `POST /merchant-onboarding/applications`, `cryptoEnvironment` must be inside `merchantData`.

### Related pages

* [Submit Application](https://developers.bead.xyz/onboarding/submit-application)
* [Sample Payload](https://developers.bead.xyz/onboarding/sample-payload)
* [Merchant Onboarding Schema](https://developers.bead.xyz/reference-guide/enumerations-and-schemas/merchant-onboarding-schema)
* [Test data: email address requirements](https://developers.bead.xyz/faqs-and-troubleshooting/environment-and-testing/test-data-email-address-requirements)
* [Test the Full Onboarding Workflow in Sandbox](https://developers.bead.xyz/onboarding/test-the-full-onboarding-workflow-in-sandbox)
