# API reference: /api/public

> The surface journal systems integrate with today: orders, clinics, settlements, subscriptions and onboarding, with the platform's and the clinic's tokens. The callback to an order's `callbackUrl` is under Webhooks.

- The spec: https://developers.rivr.io/openapi/api-public.json (Rivr API Documentation, v1, OpenAPI 3.0.4)
- The Postman collection: https://developers.rivr.io/postman/rivr-api-public.postman_collection.json
- The Postman environment for the sandbox: https://developers.rivr.io/postman/rivr-sandbox.postman_environment.json
- Page in the portal: https://developers.rivr.io/reference/api-public

The examples target the sandbox (`https://api.test.rivr.io`) and need test credentials.

## The spec's introduction

### Rivr Integration Guide

Rivr has a versatile solution specifically designed to streamline payment processes in merchant settings. This solution offers a flexible payment system, enabling customers to choose from a variety of payment methods, while allowing merchants to select and offer their preferred payment options. Additionally, this system simplifies administrative and accounting tasks for merchants by consolidating all transaction data and payment method reports into a single, comprehensive report.

#### Overview

Here follows an overview of the system capabilities.

##### Order

In this system, the fundamental element for processing payments is an `Order`. Each order represents a transaction and can be paid using multiple methods, as detailed below.

##### Methods for Paying an Order

An order can be paid in the following ways:

- Via a payment terminal at the merchant, initiated by merchant staff.
- Through self-checkout on a payment terminal, initiated by the patient.
- Online, using the customer's personal device.
- Via a traditional invoice

###### Payment Terminal

The payment terminals currently available are Android tablets with stands for reception desks, walls, or floors. The sizes of the terminals range from 9 inches to 22 inches. The payment terminals have built-in card readers, meaning these terminals replace the merchant's existing card terminals. In addition to payment, there is an arrival registration that can be turned on if the journal system supports it.

The following payment methods are currently available on the payment terminals:

 - NFC Card Payments (tap-to-pay)
 - Swish
 - Invoice
 - Instalment

###### Online Checkout

An order can be paid online. At the checkout, the following payment methods are currently available:

 - Apple Pay & Google Pay
 - Traditional Card Payment
 - Swish
 - Invoice
 - Instalment

###### Invoice as fallback

If an order does not get paid with the configured timeframe, an invoice is created and sent to the customer. This marks the order as paid. 

##### Checkout Pipeline Configuration

Merchants can configure which method or methods they want to use for their merchant. It is possible to create several configurations for different amounts, and the different methods can be configured with a delay.

**Example:** 
1. A customer can independently use a payment terminal for self-checkout.
2. If the payment is not completed, an online checkout link is sent to the customer after a predetermined duration.
3. If the order remains unpaid after an additional time period, it automatically converts into an invoice. 

###### Staff-Initiated Payment Terminal Transactions

Merchants also have the option to initiate payments through terminals, particularly beneficial in scenarios where a reception area is present for traditional payment handling.

###### Supporting Multiple Payment Terminals

Merchants with the need for more than one payment terminal are catered for. Merchant staff can select the specific terminal to display each payment. This system also allows for tracking and managing unpaid orders, enabling them to be sent to any chosen terminal.

##### Other Features

###### Comprehensive Payout System

The merchant receives consolidated payouts for all types of transactions. The frequency of these payouts can be tailored to the merchant's preferences.

###### Detailed Transaction Reports

After each payout, the merchant is provided with a detailed report. This report itemizes all transactions, categorizing them based on the payment method used.

###### Real-time Order Updates

Any changes made to an order are immediately communicated back to the originating system, ensuring up-to-date order status.

#### Integration Guide

Rivr plus utilises a REST API that revolves around the concept of an `Order`. It employs a standardized security model based on OAuth2.

Please refer to the [API documentation](https://rivr.io/docs#tag/Order) for detailed information.

##### URLs

| Test              |                               |
| ----------------- | ----------------------------- |
| Authentication    | https://auth.test.rivr.io/    |
| API               | https://api.test.rivr.io/     |

| Production        |                               |
| ----------------- | ----------------------------- |
| Authentication    | https://auth.rivr.io/         |
| API               | https://api.rivr.io/          |

##### Authentication

For a system to be able to make calls for a merchant, an `access_token` is first obtained through a call to the authentication service. An `access_token` is short-lived and its validity is indicated in the response (normally 1 hour). Clients are expected to use cache. Calls to the API without a valid `access_token` result in a response with HTTP status code 401.

You obtain `client_id` and `client_secret` from your Rivr contact.

###### Obtaining an `access_token`

```json
POST https://auth.test.rivr.io/connect/token
Content-Type: application/json

{
    "grant_type": "merchant_token",      
    "client_id": "{{clientId}}",
    "client_secret": "{{clientSecret}}",
    "merchant_id": "11112222-aaaa-bbbb-cccc-ddddeeeeffff"
}
```

Response from the authentication service:

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...(shortened)",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "merchant, order"
}
```

The body must be JSON, as above. The token endpoint does not accept the form-encoded post of RFC 6749 that tools with built-in OAuth2 support send (Swagger UI's Authorize, Postman, a generated SDK's auth helper), so request the token yourself and send it as `Authorization: Bearer`.

##### Errors

Every error answer under `/api/public` carries a body with `code`, a stable error code in snake_case, `type`, a link to the documentation for that code (`https://developers.rivr.io/errors/{code}`), and `message`, what went wrong and what to do, in English. Program against `code`. The HTTP status of an answer is the same as before the codes were added; a `204` is still a `204`. 499 is not an answer: the client has already gone.

- **The error object** (`propertyName`, `message`, `errorCode`, `code`, `type`): every error an operation itself detects. `errorCode` is the same as `code` and stays for existing integrations. `propertyName` names the field or path parameter the error is about, and is `null` when there is none.
- **The token and the scope** (`401` and `403`): the error object, plus the members these answers carried before. A token that is missing or not valid gives `401` with `token_missing`, `token_invalid`, `token_expired` or `authentication_unavailable`, and still carries `title`, `status` and `detail` (or `error` and `error_description` for a rivr-auth token). A token that is not issued for a merchant gives `merchant_token_required`, and one that is not a platform token, on the endpoints that act for a platform, `platform_token_required`. `403` carries `error` and `error_description` as before, and `code`: `invalid_scope` when the token lacks the scope the operation requires, `merchant_mismatch` when the object belongs to another merchant, `clinic_token_required` for a rivr-auth token that is not a clinic token, and `route_not_available` for an organisation API key on a route that is closed to it.
- **A request that cannot be read**: a body that is empty or malformed JSON, a value of the wrong type or a required field that is missing or `null` is rejected with `400` before the operation runs, with a validation problem: the `code` `request_invalid`, `type`, `message`, `title`, `status`, `errors` (the messages per field) and `traceId`. The onboarding link endpoints send it as `application/problem+json`, the others as `application/json`. Where an operation also answers `400` with its own error body, its `400` response says which requests get the validation problem instead. A body sent with another `Content-Type` than `application/json` gives `415` with `unsupported_media_type`, in the same form.
- **A path id that is not a UUID**: the operations that read the id as a UUID answer with the validation problem: `PUT /api/public/orders/{orderId}`, `GET /api/public/order-settlements/{orderSettlementId}` and `GET /api/public/subscriptions/{subscriptionId}`. The other order endpoints read the id themselves and answer `400` with the error object, the code `order_id_invalid` and the id's name in `propertyName`, without `errors`, except `/refund`, which answers `404` with the code `order_not_found`. The merchant endpoint answers `404` with `merchant_not_found`, and the onboarding link endpoints answer `404` with `route_not_found`, since the id is part of the route.
- **A route that does not exist**: `404` with `route_not_found`, as `application/problem+json`, also with `title`, `status` and `detail` as before. A method the route does not have gets the same answer, not `405`.
- **Rivr's own failures**: `500` with `internal_error` when something unexpected failed. Whether the request was carried out is not known, so check the order before you retry. `503` with `service_unavailable` when data Rivr needs could not be read; nothing was done, and the same request can be retried.

Every operation's responses list the codes it can answer with, and every code has a page under Error codes in the developer portal.

##### Test identities in a sandbox merchant

A Rivr sandbox merchant in the test environment accepts only test identities. `PUT /api/public/orders/{orderId}` and `POST /api/public/subscriptions` answer `422` with `errorCode` and `code` `personal_number_not_test_identity` (and `propertyName` `PersonalNumber`) when the personal number is not one. The check comes before the customer is looked up, so nothing is created and nothing about the person is returned. These are test identities:

- Skatteverket's published test personal numbers (the open dataset Testpersonnummer).
- Avarda's documented test persons, for invoice tests against Avarda's test environment.
- Synthetic Norwegian identity numbers: reserved test persons (month + 65) and Test-Norge or Tenor numbers (month + 80). Ordinary fødselsnummer, D-numbers and H-numbers (month + 40, which Norwegian health care gives real patients) belong to real people and are refused.

An order or subscription without a personal number is not affected by the check. Every other merchant behaves as before.

##### Order

To initiate a payment, an `Order` is created through the following API call. The purpose of using the HTTP verb `PUT` is to make the service idempotent. Multiple calls having the the same Id results in one `Order`.

How the order is handled in the system is configurable, but there is a checkout hint that can be supplied with the order creation in order to control the behaviour from a client application. 

This can be broken down into a two use cases.

###### 1. Let the server configuration decide

In this case, the order is created without any hints and the configuration on the server will direct how the order can be paid (device, online or invoice). This is the option to use if the merchant has chosen not to have a payment terminal. This is also the option to use if the merhant only has one payment terminal or has configured one payment terminal to be the default one. 

###### 2. Send the order to a specific device. 

In this case, a checkout hint is sent with the order creation containing the deviceId to send the payment to. This will present the order on that device, allowing it to be paid using the device. This requires that the merchant checkout pipeline has device checkout configured. If multiple calls are made with the same OrderId but with a different deviceId, the order is sent to the device in the hint. This allows for switching to another Payment terminal, if the order was sent to the wrong one in the first call.

```json
{  
  "checkoutHints": [
    {
      "type": "device",
      "value": "55556666-7777-8888-9999-aaaabbbbcccc"
    }
  ]
}
```

The `type` of a checkout hint is a string, and it is matched case-insensitively, so `device` and `Device` are the same. Earlier versions of this documentation described the type as a number. The number is still accepted, but new integrations should send the name. Responses write the type in PascalCase, for example `Device`.

##### Retrying requests safely

A request can time out or lose its response after Rivr has acted on it. These are the ways to retry without paying out or creating anything twice.

###### Creating an order

Creating an order is already idempotent through the order id that your system chooses. The id is part of the path (`PUT /api/public/orders/{orderId}`), and every request with the same id results in at most one order. If you did not get a response, send the same request again. If two requests with the same id arrive at the same time, the one that loses gets `409`, or in rare cases `500`; in both cases wait a moment and retry it with the same id. Two requests for different orders can collide the same way when they arrive at the same time for a patient who is new to Rivr. The `409` carries `Retry-After`. A new request for an order that has been cancelled or refunded opens it again as unpaid, so give every new payment a new id. The `Idempotency-Key` header is not used, and is ignored, on this endpoint.

###### Refunding, cancelling and the other operations that change something

Four operations accept an optional `Idempotency-Key` header: `POST /api/public/orders/{orderId}/refund`, `POST /api/public/orders/{orderId}/cancel`, `POST /api/public/orders/{orderId}/send-to-device` and `POST /api/public/subscriptions`. Send a new key, for example a UUID, for every refund, cancellation, send to a device or subscription request, and send the same key again when you retry that request. The onboarding link operations (`POST /api/public/onboarding-links` and `POST /api/public/onboarding-links/{id}/refresh`) do not use the header and ignore it, because their responses carry the clinic's organisation number and a link to the signing, which Rivr does not store for replay.

```
POST https://api.test.rivr.io/api/public/orders/{orderId}/refund
Authorization: Bearer {{access_token}}
Idempotency-Key: 3f1c2a7e-8b4d-4e6f-9a0b-1c2d3e4f5a6b
```

The same header on a cancellation:

```
POST https://api.test.rivr.io/api/public/orders/{orderId}/cancel
Authorization: Bearer {{access_token}}
Idempotency-Key: {{a new UUID for this cancellation}}
```

And on a request that has a body, for example when you send an order to a device:

```
POST https://api.test.rivr.io/api/public/orders/{orderId}/send-to-device
Authorization: Bearer {{access_token}}
Content-Type: application/json
Idempotency-Key: {{a new UUID for this send}}

{ "deviceId": "55556666-7777-8888-9999-aaaabbbbcccc" }
```

- The key is 1 to 255 printable ASCII characters. Use a random value, and never put personal data in it. It is scoped to your API client (and the merchant, when the token is issued for one, or your platform) and to the endpoint, so another client using the same key is not affected, and a key used on `/refund` is not the same key on `/cancel`. A token issued with merchant credentials only, without a client, is scoped to the merchant.
- The first response with a 2xx status is stored for 24 hours. A request with the same key, the same resource in the path (for example the same order) and the same body gets the stored response again, with the response header `Idempotent-Replayed: true`, and nothing is refunded, cancelled, sent to the device or created a second time. The body is compared as it was sent, byte for byte, so send a retry exactly as the first request.
- A replay is the response of the first request as it was then. It is not updated, so read the current state with `GET /api/public/orders/{orderId}` or `GET /api/public/subscriptions/{subscriptionId}`. A replayed `POST /api/public/subscriptions` has the same status, body and `Location` header as the first response.
- For a cancellation this holds also when the order has been opened again with `PUT /api/public/orders/{orderId}` in between: a retry with the old key gets the stored `204` and does not cancel the reopened order. Without the header, a retry of `/cancel` is safe only as long as the order stays cancelled, since an order that is already cancelled gives `204`.
- If the request is refused, for example `400` because the order cannot be refunded, `409` with `payment_in_progress`, or `409` with `device_offline`, nothing is stored. You can retry with the same key once the cause is gone.
- Two requests with the same key never refund, cancel, send or create twice, even when they arrive at the same time. The one that arrives while the first is still being processed gets `409` with `errorCode` `idempotency_in_progress` and a `Retry-After` header. Retry with the same key.
- If Rivr is interrupted while it processes a request, for example during a deployment, its key can keep answering `409` `idempotency_in_progress` for up to 24 hours. If the `409` persists for more than a few minutes, check the order or the subscription with the `GET` operation before you retry with a new key.
- After 24 hours the key is forgotten, and a request with it is handled as a new request.

With the header, the response can also carry these `errorCode` values. `propertyName` is then `Idempotency-Key`.

| Status | `errorCode`                   | Meaning and what to do |
| ------ | ----------------------------- | ---------------------- |
| 400    | `idempotency_key_invalid`     | The header is empty, longer than 255 characters, contains other characters than printable ASCII, or is sent more than once, or the token identifies neither an API client nor a merchant. Nothing was done. |
| 409    | `idempotency_in_progress`     | A request with the same key is still being processed. Retry shortly with the same key to get its response. It can last for up to 24 hours after an interruption; if it persists for more than a few minutes, check the order or subscription before you retry with a new key. |
| 422    | `idempotency_mismatch`        | The key has already been used for another order or with another body. Nothing was done. Use a new key. `POST /api/public/subscriptions` can also answer `422` with `personal_number_not_test_identity`, which has nothing to do with the key. |
| 500    | `idempotency_outcome_unknown` | The request failed after the change may have been started: the refund, cancellation, send to the device or subscription. The same key gives this answer for 24 hours. Check the order with `GET /api/public/orders/{orderId}` or the subscription with `GET /api/public/subscriptions/{subscriptionId}` before you retry with a new key. |
| 503    | `idempotency_unavailable`     | The key could not be checked, so nothing was done. Retry with the same key. |

A request without the header behaves exactly as before. Partial refunds are not available in this API.

#### Callbacks

The integration is based on the calling system being able to receive callbacks (webhooks). The callback contains the orderId and the `OrderStatus` describing the event that occurred. Depending on the event, there may be more information provided. The callback is delivered to the `callbackUrl` that is set when creating the order.

Rivr switches a rule on per environment, starting with the sandbox: the `callbackUrl` must then be an absolute `https` URL. The rule is that the URL:

- is at most 2048 characters long,
- has no user info (`https://user:password@host`) and no fragment (`#...`), and
- does not point to `localhost`, a name under `.localhost` or an IP address in a loopback, private, carrier-grade NAT, link-local, multicast or unique-local range (for example `127.0.0.1`, `10.x.x.x`, `192.168.x.x` or `[::1]`).

The host is judged in the form Rivr connects to (its IDNA form, so a name written with other Unicode characters counts as the address it becomes). A hostname is not resolved when the order is created, so a name is accepted unless it is `localhost` or under `.localhost`. `PUT /api/public/orders/{orderId}` answers `422` with `errorCode` and `code` `callback_url_invalid` (and `propertyName` `CallbackUrl`) when the URL breaks the rule, and the order is not created or changed. An order without a `callbackUrl` is not affected. Where the rule is not switched on, `callbackUrl` is accepted as before.

Example of a callback body:

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Created",
  "data": {
    "createdDate": "2024-08-19 14:58:21"
  }
}
```

If the system is unable to receive callbacks, the `GetOrderAsync` operation can be used periodically to check the status of an order.

##### Callback details

Callbacks from the Rivr systems are delivered using a HTTP POST to the URL supplied in the initial creation of the object.

A response with an HTTP status code in the range 200-299 is considered successful, and no more attempts are made.

If the callback isn't successful, it is retried. Rivr makes at most 14 attempts within seven days. Every attempt is scheduled from the time the event was created, not from the previous attempt, so a delayed attempt does not push back the ones after it:

| Attempt | Time after the event was created |
| ------- | -------------------------------- |
| 1       | Immediately                      |
| 2       | 1 minute                         |
| 3       | 5 minutes                        |
| 4       | 30 minutes                       |
| 5       | 1 hour                           |
| 6       | 4 hours                          |
| 7       | 12 hours                         |
| 8       | 1 day                            |
| 9       | 2 days                           |
| 10      | 3 days                           |
| 11      | 4 days                           |
| 12      | 5 days                           |
| 13      | 6 days                           |
| 14      | 7 days                           |

Retries are picked up by a job that runs once a minute, so an attempt is normally made within a minute after the time in the table.

- Each attempt has a timeout of 5 seconds. An attempt that times out or cannot connect is not successful and is retried.
- A response with status code 400, 405, 410, 413, 414, 415 or 422 means that the receiver rejected the payload itself. Sending the same payload again cannot succeed, so no more attempts are made.
- Any other response, for example 401, 404, 429 or 5xx, is retried according to the table.

If the 14th attempt is not successful, no more attempts are made.

Note: If the Callback URL is empty or malformatted there will be no attempts at all.

###### Verifying callbacks (`Rivr-Signature`)

When the merchant or its platform has an active webhook signing key, every callback carries a `Rivr-Signature` header:

```
Rivr-Signature: t=1729245600,v1=5f2b9c0e…,v1=0a1b2c3d…
```

`t` is the time of the attempt in Unix seconds. Each `v1` is the lowercase hex HMAC-SHA256 of `{t}.{body}` (the timestamp, a dot and the raw request body exactly as received) with one of the active signing keys. There is one `v1` per active key on the merchant and on its platform, so a key can be rotated without a gap.

To verify a callback:

1. Read `t` and every `v1` from the header.
2. Compute the HMAC-SHA256 of `{t}.{body}` with each signing key you hold.
3. Accept the callback if any `v1` matches, compared in constant time, and reject it if `t` is too old (for example more than five minutes).

Signing is optional on this delivery path: a merchant without an active signing key gets its callbacks unsigned.

###### The Rivr-Webhook-Id header

Every callback carries a `Rivr-Webhook-Id` header that identifies the event it describes:

```http
POST /rivr/callbacks HTTP/1.1
Content-Type: application/json; charset=utf-8
Rivr-Webhook-Id: 0f8e5a52-7c1d-5b3e-9a4f-6d2c8b1e7a90
```

- Every attempt to deliver the same event carries the same value, including the retries in the table above. Store the values you have handled, and answer a callback whose `Rivr-Webhook-Id` you have already handled with a 2xx status code without handling it again.
- Two different events have different values, also when they concern the same order and have the same status. An order that is created and later reopened sends `Created` twice, with two different values.
- Treat the value as an opaque string. Today it is a UUID.
- The header does not change the body. It is not part of the signed content: when a callback carries a `Rivr-Signature` header, the signature covers the timestamp and the body only.

###### Order

Here follows a detailed overview of the callbacks related to the Order entity.

###### Created

Immediately after an order is created a callback is issued.

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Created",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z"
  }
}
```

###### Completed

A order is considered `Completed` when it has been paid. An Order can be paid with a different payment methods. The different variants of callbacks are listed here.

###### Card

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "Card"
  }
}
```

###### Apple Pay / Google Pay

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "DigitalWallet"
  }
}
```

###### Swish

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "Swish"
  }
}
```

###### Invoice

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "Invoice"
  }
}
```

###### Instalment

Instalment comes in three flavours, Default, Full KYC and Interest Free.

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "InstalmentDefault"
  }
}
```

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "InstalmentFullKyc"
  }
}
```

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Completed",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CompletedDate": "2024-04-24T16:45:52.54Z",
    "PaymentMethod": "InstalmentInterestFree"
  }
}
```

###### Cancelled

Orders can be cancelled.

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Cancelled",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "CancelledDate": "2024-04-24T16:45:52.54Z",
    "Reason": "Timeout | UserCancelled | MerchantCancelled"
  }
}
```

###### Refunded

An order can be refunded and when successful this callback is sent.

```json
{
  "id": "84c3c4ad-63d8-4d49-a4ff-ed57bbd40a39",
  "type": "Order",
  "status": "Refunded",
  "data": {
    "CreatedDate": "2024-04-24T16:45:30.582Z",
    "RefundedDate": "2024-04-24T16:45:52.54Z"
  }
}
```




###### Loan Applications
  When creating a Loan Application, a callback URL can be supplied. When the Loan Application changes state, a HTTP POST will be performed to the supplied URL having the following body: (`Terms` and `SignLink` are only included if the status is `Granted`)

```json
{
    "LoanApplciationId": "00000000-0000-0000-0000-000000000000",
    "Status": "Granted",
    "Terms": {
        "Amount": 0,
        "AmountToRefinance": 0,
        "NominalRate": 0,
        "EffectiveRate": 0,
        "RepaymentTime": 0,
        "MonthlyPayment": 0,
        "StartupFee": 0,
        "MonthlyFee": 0,
        "AdministrationFee": 0,
        "InvoiceFee": 0,
        "TotalCost": 0,
    }
    "SignLink": "https://url-to-sign-link.com"
}
```

`LoanApplicationId` has the same value as when the LoanApplication was created.

`Status` may be one of the following:

```
    "Created",
    "Submitted",
    "Pending",
    "Granted",
    "AgreementSigned",
    "PaidOut",
    "Denied",
    "Expired",
```

## Servers

- https://api.test.rivr.io: Test (sandbox)
- https://api.rivr.io: Production

## Tokens

### `Bearer`

OAuth2 client credentials with one difference from RFC 6749: `https://auth.test.rivr.io/connect/token` only accepts a **JSON** body (`Content-Type: application/json`), not the form-encoded post that tools with built-in OAuth2 support send (Swagger UI's Authorize, Postman, a generated SDK's auth helper). Request the token yourself: POST `client_id`, `client_secret` and `grant_type` as JSON (`client_credentials` for a platform token, or `merchant_token` with `merchant_id` to act for one merchant) and send the `access_token` as `Authorization: Bearer`. A token lasts about an hour; cache it. The scopes are set on your client by Rivr, and each operation lists the scope it requires.

- clientCredentials: tokenUrl https://auth.test.rivr.io/connect/token

  - `public:devices:read`: Read the merchant's devices (payment terminals)
  - `public:health:read`: Check that the token works, and read the integration's onboarding progress
  - `public:merchants:read`: Read merchants
  - `public:onboarding:read`: Read the status of your clinic onboardings (platform token)
  - `public:onboarding:write`: Start and refresh clinic onboardings (platform token)
  - `public:order-settlements:read`: Read order settlements (payouts)
  - `public:orders:read`: Read orders, order status and receipts
  - `public:orders:status`: Read order status only
  - `public:orders:write`: Create, update, refund, cancel and send orders to a device
  - `public:subscriptions:read`: Read subscriptions
  - `public:subscriptions:write`: Create and update subscriptions

## Operations

### Health

- [GET /api/public/health](https://developers.rivr.io/reference/api-public/getHealth.md): Health check
- [GET /api/public/health/client-ip](https://developers.rivr.io/reference/api-public/getClientIp.md): Client IP address
- [GET /api/public/health/ready](https://developers.rivr.io/reference/api-public/getReadiness.md): Readiness check
- [GET /api/public/health-secure](https://developers.rivr.io/reference/api-public/getHealthSecure.md): Health check (authenticated)

### OnboardingLinks

- [GET /api/public/onboarding-links](https://developers.rivr.io/reference/api-public/getOnboardingLinks.md): List onboarding links
- [POST /api/public/onboarding-links](https://developers.rivr.io/reference/api-public/createOnboardingLink.md): Create onboarding link
- [GET /api/public/onboarding-links/{id}](https://developers.rivr.io/reference/api-public/getOnboardingLink.md): Retrieve onboarding link
- [POST /api/public/onboarding-links/{id}/refresh](https://developers.rivr.io/reference/api-public/refreshOnboardingLink.md): Refresh onboarding link

### Device

- [GET /api/public/devices](https://developers.rivr.io/reference/api-public/getDevices.md): Get all devices for the current merchant

### Merchant

- [GET /api/public/merchants](https://developers.rivr.io/reference/api-public/listMerchants.md): Retrieve a paged list of Merchants
- [GET /api/public/merchants/{merchantId}](https://developers.rivr.io/reference/api-public/getMerchant.md): Retrieve a Merchant by MerchantId

### Onboarding

- [GET /api/public/onboarding/progress](https://developers.rivr.io/reference/api-public/getOnboardingProgress.md): Retrieve onboarding progress for the authenticated platform

### Order

- [GET /api/public/orders/{orderId}](https://developers.rivr.io/reference/api-public/getOrder.md): Retrieve Order
- [PUT /api/public/orders/{orderId}](https://developers.rivr.io/reference/api-public/createOrder.md): Create Order
- [GET /api/public/orders/{orderId}/status](https://developers.rivr.io/reference/api-public/getOrderStatus.md): Retrieve Order Status
- [GET /api/public/orders/{orderId}/receipts/{receiptId}](https://developers.rivr.io/reference/api-public/getOrderReceipt.md): Retrieve Order Receipts
- [POST /api/public/orders/{orderId}/refund](https://developers.rivr.io/reference/api-public/refundOrder.md): Refund Order
- [POST /api/public/orders/{orderId}/cancel](https://developers.rivr.io/reference/api-public/cancelOrder.md): Cancel Order
- [POST /api/public/orders/{orderId}/send-to-device](https://developers.rivr.io/reference/api-public/sendOrderToDevice.md): Send Order to Device

### OrderSettlement

- [GET /api/public/order-settlements](https://developers.rivr.io/reference/api-public/listOrderSettlements.md): Retrieve a paged list of OrderSettlements
- [GET /api/public/order-settlements/{orderSettlementId}](https://developers.rivr.io/reference/api-public/getOrderSettlement.md): Retrieve Order Settlement
- [GET /api/public/order-settlements/next-unread](https://developers.rivr.io/reference/api-public/getNextUnreadSettlement.md): Retrieve Next Unread Order Settlement

### Subscription

- [POST /api/public/subscriptions](https://developers.rivr.io/reference/api-public/createSubscription.md): Creates or updates a subscription
- [GET /api/public/subscriptions/{subscriptionId}](https://developers.rivr.io/reference/api-public/getSubscription.md): Get a subscription by id

### Webhooks

- [POST {callbackUrl} (callback)](https://developers.rivr.io/reference/api-public/receiveCallback.md): Callback to your callbackUrl
