---
agentTools:
  projectIndex: https://docs.ninepay.com.br/llms.txt
  projectSearch: https://docs.ninepay.com.br/agentSearch?query=term1%20term2
---

# API Introduction

Environments, authentication, response and error formats, pagination and card hashes

Welcome to the **NinePay API** reference. This API is how you integrate your system with ours.

Our API is [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) and all of its responses are in [JSON](https://www.json.org/), at the base endpoint:

```text Production
https://payments.cefis.com.br/api/v1/
```

The full list of endpoints, with parameters, responses and examples in several languages, is in the [API Reference](/reference/criar-novo-pagamento).

## Environments

In addition to the **production** environment, a **sandbox** environment is available for integration testing. All communication with the sandbox environment must use the URL:

```text Sandbox
https://api.sandbox.cefispay.com.br/api/v1/
```

See how to request access in [Test Environment](/docs/sandbox-en).

## Authentication

All requests that require authentication must send, in the `Authorization` header, the API token provided by our team, always for the intended environment, in the following format:

```http
Authorization: Bearer <token>
```

> ❗️ The token is a secret
>
> Use the token only in your back end. Never put it in code that runs in the browser or in your customer's app.

Key prefixes (`sk_prod_` and `sk_hmlg_`), creation, rotation and expiration are covered in [Authentication](/docs/authentication).

## Headers

We recommend setting the request headers correctly to work with the data types sent to and received from the API:

- All requests must include the `Content-Type: application/json` header, unless the specific endpoint says otherwise.
- All requests must include the `Accept: application/json` header, unless the specific endpoint says otherwise.

## Responses

- Resources come inside `data`: `GET /customers/{id}` responds with `{ "data": { ... } }`.
- Creations respond with **201**. `POST /payments` responds with `{ "customer", "payment", "invoice_url" }` (`invoice_url` only for bank slips) and `POST /subscriptions` responds with `{ "customer", "subscription" }`.
- Amounts are always in cents (integer): R$ 50.00 is `5000`.

## Errors

Every error has the same format:

```json
{
  "code": "VALIDATION_ERROR",
  "message": "Os dados informados são inválidos.",
  "errors": { "email": ["O campo email é obrigatório."] },
  "timestamp": "2026-10-22T12:03:54+00:00",
  "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032",
  "documentation_url": "https://docs.ninepay.com.br",
  "suggestion": "Corrija os campos e tente novamente."
}
```

`errors` is only present on validation errors, with a **list** of messages per field. `suggestion` is present when there is one. Messages are returned in Portuguese. Keep the `request_id` when contacting support.

| `code` | HTTP | When |
| --- | --- | --- |
| `AUTHENTICATION_ERROR` | 401 | Missing or invalid token |
| `FORBIDDEN_ERROR` | 403 | The resource does not belong to your company. On several routes, an id that does not exist also responds with 403, so as not to reveal which ids exist |
| `COMPANYBLOCKED_ERROR` | 403 | The account is suspended |
| `RESOURCE_NOT_FOUND_ERROR` | 404 | The resource does not exist |
| `ROUTE_NOT_FOUND_ERROR` | 404 | The route does not exist |
| `METHOD_NOT_ALLOWED_ERROR` | 405 | The method is not valid for the route (e.g. `PUT /customers` without the id) |
| `INVALID_STATE_ERROR` | 400 | The resource is not in a state that allows the action (e.g. canceling a payment that is already paid) |
| `VALIDATION_ERROR` | 422 | Invalid data; see `errors` |
| `REFUND_NOT_ALLOWED` | 422 | The payment cannot be refunded |
| `TOO_MANY_ATTEMPTS` | 429 | Too many requests; respect the `Retry-After` header |
| `UNEXPECTED_ERROR` | 500 | Our error; if it happens again, contact support with the `request_id` |

## Pagination

List endpoints accept `page` (starting at 1) and `limit` (items per page: **10** by default, from 1 to **100**). The response contains `data` (the items), `links` and `meta` (`current_page`, `last_page`, `per_page`, `total`). To go through everything, increase `page` up to `meta.last_page`.

## Dates

- Calendar dates (`due_date`, `next_payment`) are sent as `YYYY-MM-DD` in requests.
- In responses, date-time fields come in two formats: ISO 8601 in UTC (`2026-10-27T03:00:00.000000Z`, in the payment's `due_date` and `ref_date`) and `YYYY-MM-DD HH:MM:SS` in Brasília time (`created_at` and `updated_at`). Parse each field according to its format.

## Identifiers

Every resource has a numeric `id` and a `uuid`. Use the `id` in authenticated routes and the **`uuid`** in everything that is public (the payment link, the checkout and the bank slip PDF).

## Card hash

A card hash is an encrypted version of all of your customer's card data. It is the safest way to send your customer's card data directly to us. Below we explain how it works and how to obtain a hash from the data entered by the customer.

> 🚧 Front end only
>
> This flow must run on the front end, whether web or mobile. Your customer's card data must never be sent to your back end; the card hash, however, can be sent to the back end safely.

### Generating an encryption key

The encryption key is an RSA public key used to encrypt the card data. This key can be used only once: whenever you need to encrypt a new card, generate a new encryption key.

To generate an encryption key, make a `POST` request to the API's `/encryption_keys` route, sending your `merchant_id` as a parameter. It does not use the API token (never put it on the front end): the `merchant_id` identifies your account, and a `merchant_id` that does not exist responds with 401.

The request must have this format:

```bash cURL
curl -X POST https://payments.cefis.com.br/api/v1/encryption_keys \
  -H 'Content-Type: application/json' \
  -d '{"merchant_id":"SEU_MERCHANT_ID"}'
```

In response you will receive a JSON object in the following format:

```json
{
  "id": 1111,
  "public_key": "-----BEGIN PUBLIC KEY-----\n ... \n-----END PUBLIC KEY-----\n",
  "ip_address": "000.000.000.000",
  "created_at": "2017-07-03"
}
```

### Encrypting the card data

To encrypt the card data, first format it correctly: create a JSON string containing all the card data in the following format:

```json
{"card_number":"1111111111111111","card_holder_name":"Jose Silva","card_expiration_date":"11/2021","card_cvv":"111"}
```

Then RSA-encrypt this string using the public key (`public_key`) generated in the previous step, with **PKCS1** padding.

After encrypting the data, convert the result to base64. You will have something like this:

```text
GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==
```

The last step is to concatenate the `id` of your encryption key (received in the response to the previous request) with the base64 string that represents the card data, as follows:

```text
encryption_key_id + "_" + base64_encrypted_card
```

The result is your card hash:

```text
1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==
```

Done: your customer's card data is now in a safe format to travel over the network and can be sent to your back end to continue the flow (in the `card_hash` field of [`POST /payments`](/reference/criar-novo-pagamento)).

> 📘 Card hash validity
>
> Each card hash can be used only once and is valid for at most 30 minutes after the encryption key is generated. If you need to use the card more than once, make a request to the cards route and save the card linked to the user who owns it: you will receive a unique ID that can be used in new transactions with the same card.

## Support

Questions about the integration: [ti@ninepay.com.br](mailto:ti@ninepay.com.br). Terms of use: [bit.ly/cefispay-contrato](https://bit.ly/cefispay-contrato).
