> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nymblecommerce.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Nymble Commerce REST API Reference — Overview and Conventions

> Complete reference for the Nymble Commerce REST API. Base URLs, authentication headers, request format, pagination, and response conventions.

The Nymble Commerce API is a REST API that accepts JSON request bodies and returns JSON responses. All endpoints require authentication except the login and public address lookup endpoints. You interact with the API by sending HTTP requests to a base URL and including your credentials in the request headers.

## Base URLs

Use the production base URL for live traffic and the sandbox base URL for development and testing.

| Environment | Base URL                              |
| ----------- | ------------------------------------- |
| Production  | `https://api.achievemomentum.com`     |
| Sandbox     | `https://api-dev.achievemomentum.com` |

<Tip>
  Point your integration at the sandbox environment first. The sandbox is a full copy of the production API with isolated data, so you can test freely without affecting live records.
</Tip>

## Authentication

The Nymble Commerce API supports two authentication methods. Use whichever method suits your integration.

### Bearer token (JWT)

After you call `POST /api/auth/login`, you receive a JWT access token. Pass it in the `Authorization` header on every subsequent request:

```bash theme={null}
curl https://api.achievemomentum.com/api/organization \
  -H "Authorization: Bearer {your_access_token}"
```

### API key

Some machine-to-machine integrations use a static API key instead of a short-lived JWT. Pass it in the `x-api-key` header:

```bash theme={null}
curl https://api.achievemomentum.com/api/organization \
  -H "x-api-key: {your_api_key}"
```

<Note>
  You can obtain an API key by calling `POST /api/organizations/api-access` from an Admin-scoped token. API keys do not expire but should be rotated periodically and stored securely — treat them like passwords.
</Note>

## Request format

Send all request bodies as JSON and set the `Content-Type` header accordingly:

```bash theme={null}
curl -X POST https://api.achievemomentum.com/api/users \
  -H "Authorization: Bearer {your_access_token}" \
  -H "Content-Type: application/json" \
  -d '{"FirstName": "Jane", "LastName": "Smith", "Email": "jane@example.com"}'
```

**Exceptions:** File upload endpoints use `multipart/form-data`. The reference page for each such endpoint calls this out explicitly.

## Response format

Successful responses use standard HTTP status codes:

| Code             | Meaning                                                                        |
| ---------------- | ------------------------------------------------------------------------------ |
| `200 OK`         | The request succeeded. The response body contains the result.                  |
| `201 Created`    | A new resource was created. The response body contains the created object.     |
| `204 No Content` | The request succeeded but there is no response body (e.g. a delete operation). |

Resource endpoints return the created or updated object directly. List endpoints return an array wrapped in a response envelope. A typical successful response looks like this:

```json theme={null}
{
  "data": [...],
  "totalCount": 42
}
```

A typical single-resource response:

```json theme={null}
{
  "organizationId": "org_abc123",
  "name": "Acme Corp",
  "slug": "acme-corp"
}
```

## Organization scoping

Nymble Commerce is a multi-tenant platform. Every request is automatically scoped to your organization via claims embedded in your JWT. You do **not** need to pass `OrganizationId` as an explicit body or query parameter on most endpoints — the API reads it directly from your token.

```text theme={null}
JWT claims (set automatically)
├── OrganizationId  → scopes data to your org
├── PriceTier       → applied to pricing lookups
├── CurrencyCode    → applied to monetary values
└── CustomerNumber  → linked customer account
```

Some admin-path endpoints include the organization identifier in the URL for clarity (e.g. `GET /api/organization`). In those cases the path parameter or query value must match the `OrganizationId` in your token, unless you hold a **Super** role.

## Pagination

List endpoints accept the following query parameters:

| Parameter    | Type    | Default | Description                |
| ------------ | ------- | ------- | -------------------------- |
| `PageNumber` | integer | `1`     | Page number (1-indexed)    |
| `PageSize`   | integer | `25`    | Number of records per page |

The response includes the current page of records alongside a total count so you can calculate total pages client-side:

```json theme={null}
{
  "Data": [
    { "...": "..." }
  ],
  "TotalCount": 150,
  "PageNumber": 1,
  "PageSize": 25
}
```

<Tip>
  To iterate all records, keep requesting the next page until the length of `Data` is less than `PageSize`.
</Tip>

## Rate limits

| Endpoint                                        | Limit                               |
| ----------------------------------------------- | ----------------------------------- |
| `POST /api/auth/login`                          | 10 requests / minute per `ClientId` |
| Public endpoints (e.g. `GET /api/users/exists`) | 10 requests / minute per IP         |
| All other authenticated endpoints               | No hard limit (fair use applies)    |

When you exceed a rate limit the API returns `429 Too Many Requests`. Retry after the interval indicated in the `Retry-After` response header.

<Warning>
  Cache your access token and reuse it until it expires rather than authenticating on every request. Repeated login calls using the same `ClientId` are throttled aggressively.
</Warning>

## Idempotency

For bulk-create endpoints, passing an `ExternalId` field makes the operation idempotent. If you send the same `ExternalId` more than once, the API updates the existing record rather than creating a duplicate:

```json theme={null}
{
  "ExternalId": "your-system-id-9001",
  "CustomerNumber": "C-10042",
  "UserId": "kp_abc123"
}
```

This lets you safely retry failed requests without worrying about creating duplicate records.

## Error handling

When a request fails, the API returns a problem details object following [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457). See the [Error Reference](/errors) page for the full list of error codes and guidance on handling each one.

A typical error response:

```json theme={null}
{
  "type": "https://httpstatuses.com/400",
  "title": "Bad Request",
  "status": 400,
  "errors": [
    {
      "name": "Email",
      "reason": "'Email' must not be empty."
    }
  ]
}
```

| Field    | Description                                               |
| -------- | --------------------------------------------------------- |
| `status` | The HTTP status code                                      |
| `title`  | A short human-readable summary of the error               |
| `errors` | Array of field-level validation failures, when applicable |
