<!-- Sister file: api_header.md (consumed by apidoc for the legacy HTML site). Keep content in sync when conventions change. -->

# Brivo API Overview

This document covers the conventions shared by every Brivo Access API endpoint:
base URLs, authentication, schema, paging, and error codes. For per-endpoint
details, see the per-tag references listed in `access-api-context.yaml`.

## Table of Contents

- [Base URLs](#base-urls)
- [Authentication](#authentication)
  - [Headers](#headers)
  - [OAuth2 Tokens](#oauth2-tokens)
- [Schema](#schema)
  - [Date and Time](#date-and-time)
  - [Empty Values](#empty-values)
- [Listing Calls](#listing-calls)
  - [Results](#results)
  - [Paging](#paging)
  - [Filters](#filters)
- [Response Codes](#response-codes)
  - [Success Codes](#success-codes)
  - [Error Codes](#error-codes)
- [Notifications](#notifications)
  - [TLS 1.0 Deprecation](#tls-10-deprecation)

## Base URLs

Brivo accounts exist in one of two environments. The base PROD environment
serves accounts accessed via `access.brivo.com`. The EU instance serves
accounts accessed via `access.eu.brivo.com`. Use the matching base URLs when
calling the API.

### PROD

| Name | URL |
| --- | --- |
| Auth | `https://auth.brivo.com` |
| API  | `https://api.brivo.com`  |

### EU

| Name | URL |
| --- | --- |
| Auth | `https://auth.eu.brivo.com` |
| API  | `https://api.eu.brivo.com`  |

## Authentication

The Brivo API uses the OAuth2 3-legged `authorization_code` workflow. Most
languages have OAuth2 client libraries; a list is at <https://oauth.net/code/>.
Internally, Brivo uses Spring Security OAuth for Java clients.

### Authentication URLs

| Name | URL |
| --- | --- |
| Authorize | `{baseURL}/oauth/authorize` |
| Token     | `{baseURL}/oauth/token`     |

### Headers

Calls to `api.brivo.com` require a Mashery API key and an Authorization header:

| Header        | Value                       |
| ------------- | --------------------------- |
| api-key       | `API_KEY`                   |
| Authorization | `bearer ACCESS_TOKEN_VALUE` |

API keys are managed at the [developer portal](https://developer.brivo.com/apps/mykeys).
Keys are environment-specific. PROD test keys can be generated by self-service;
EU test keys: contact <api@brivo.com>.

The `Authorization` bearer value is the `access_token` returned by the auth
server after a successful authentication.

Calls to `auth.brivo.com` require this Authorization header:

| Header        | Value                    |
| ------------- | ------------------------ |
| Authorization | `Basic CLIENT_SIGNATURE` |

`CLIENT_SIGNATURE` is the base64 encoding of `CLIENT_ID:CLIENT_SECRET`.

```text
CLIENT_ID=ABC-123-456
CLIENT_SECRET=qwerty98765

Before base64 encode:
ABC-123-456:qwerty98765

After encoding:
QUJDLTEyMy00NTY6cXdlcnR5OTg3NjU=
```

A Client ID and Secret are obtained by creating an Application in the Brivo
sandbox account provided when registering as a developer. Client credentials
must exist in the same environment as the account being connected.

To create an Application:

1. Log in to Access.
1. Navigate to Marketplace.
1. Click **Generate API Token**.
1. Fill in Application Name, Description, and Type.
1. Fill in Redirect URI if using 3-Legged.
   - 3-Legged enables the `authorization_code` grant type and 3-legged OAuth flow.
   - Password allows providing an admin login and password directly, but
     restricts the application to the owner's account.
1. Submit. Make sure the `redirect_uri` is valid for 3-Legged applications.
1. Once created, click the Application Details icon to view the Client ID
   and Secret.

#### Request Body Content-Type

Requests to `api.brivo.com` that submit a body should set `Content-type`. Safe
to include even when there is no body.

| Header       | Value              |
| ------------ | ------------------ |
| Content-type | `application/json` |

Requests to `auth.brivo.com` that include body parameters must use form data;
JSON is not supported. Supported `Content-type` values:

| Header       | Value                                 |
| ------------ | ------------------------------------- |
| Content-type | `application/x-www-form-urlencoded`   |
| Content-type | `multipart/form-data`                 |

### OAuth2 Tokens

#### 3-legged authorization_code grant

Step 1 — start the flow:

```http
GET https://auth.brivo.com/oauth/authorize

Authorization: Basic {BASE64_ENCODED_CLIENT_CREDENTIALS}
api-key:       {api-key from developer.brivo.com}
Content-type:  application/x-www-form-urlencoded

Body:
response_type=code
client_id={CLIENT_ID}
state={providedState}
```

The `state` parameter is optional. The connecting application generates and
tracks it; it is returned with the `code` to the redirect URI so the
application can correlate code responses with originating requests.

The `/oauth/authorize` request redirects to the Brivo login screen. After
successful login, a POST is sent to the redirect URI registered with the
application. The POST URL includes `?code={AUTHORIZATION_CODE}` (and
`&state={providedState}` if one was supplied).

Step 2 — exchange the authorization code for an access token:

```http
POST https://auth.brivo.com/oauth/token

Authorization: Basic {BASE64_ENCODED_CLIENT_CREDENTIALS}
api-key:       {api-key from developer.brivo.com}
Content-type:  application/x-www-form-urlencoded

Body:
grant_type=authorization_code
code={AUTH_CODE}
redirect_uri={REDIRECT_URI}
client_id={CLIENT_ID}
client_secret={CLIENT_SECRET}
```

Use the returned `access_token` in the `Authorization` header for subsequent
API calls. It is valid for `expires_in` seconds.

#### Password grant

Same headers as above:

```http
POST https://auth.brivo.com/oauth/token

Body:
grant_type=password
username={ADMIN_USERNAME}
password={ADMIN_PASSWORD}
```

#### Token response

```json
{
  "access_token": "[ACCESS_TOKEN_VALUE]",
  "token_type": "bearer",
  "refresh_token": "[REFRESH_TOKEN_VALUE]",
  "expires_in": 300
}
```

After the access token expires, use the refresh token to request a new one
without going through the full authorization flow — until the refresh token
also expires.

#### Refreshing a token

```http
POST https://auth.brivo.com/oauth/token

Body:
grant_type=refresh_token
refresh_token={REFRESH_TOKEN}
```

## Schema

> **Reminder:** URL-encode any data submitted via query parameters; values may
> contain characters invalid in a URL.

### Date and Time

All datetimes are ISO 8601 to second precision. Milliseconds are not provided.

```text
YYYY-MM-DDTHH:MM:SSZ
```

All returned datetimes are UTC. Example: `2015-04-15T12:00:00Z`.

Inputs may include a timezone offset, and the API adjusts accordingly. Example:
`2015-04-15T17:00:00-05:00`.

### Empty Values

Empty or null fields are omitted from JSON responses, not returned as `null`.

Listing calls often return a smaller slice of fields than the per-object
retrieval endpoint.

## Listing Calls

Listing calls are available for most domain groups. Most support optional
pagination and filter parameters.

### Results

```json
{
  "data": [ "List of domain objects" ],
  "offset": 0,
  "pageSize": 20,
  "count": 0
}
```

### Paging

#### offset

`offset` skips the first N results. Default: `offset=0`.

```text
/users?offset=15
```

#### pageSize

`pageSize` caps the number of results returned. Default: `20`. Max: `100`.

```text
/users?pageSize=50
```

### Filters

The `filter` parameter accepts filters for endpoints that support filtering:

```text
<filterName>__<operator>:<filterValues>
```

- `__` separates field name and operator.
- `:` separates operator and value(s).
- Multiple values are comma-separated when the filter allows it.
- Multiple filters are separated by `;`.

| Operator | Description  |
| -------- | ------------ |
| eq       | equals       |
| ne       | not equals   |
| gt       | greater than |
| lt       | less than    |

```text
?filter=id__eq:11,22,33;name__eq:Brivo
```

## Response Codes

### Success Codes

| Code | Description |
| ---- | ----------- |
| 200  | Successful. GET responses include the requested JSON; create/update responses generally include the updated domain object. |
| 204  | Successful with no response body. Typical for delete or relationship-update operations. |

### Error Codes

| Code | Description |
| ---- | ----------- |
| 400  | Bad Request — invalid request parameters. Body: `{ "code": 400, "message": "..." }`. |
| 401  | Unauthorized — missing or invalid credentials/OAuth token. Common bodies: `{"error":"invalid_grant","error_description":"Bad credentials"}` (bad username/password); `{"status":401,"error":"Unauthorized","message":"Bad credentials","path":"/oauth/token"}` (bad Basic auth header); `{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}` (no Basic auth header). |
| 403  | Forbidden — caller lacks permission for the request. Body: `{ "code": 403, "message": "..." }`. A `<h1>Developer Inactive</h1>` body indicates a missing or invalid `api-key` header. |
| 404  | Not Found. Body: `{ "code": 404, "message": "..." }`. |
| 415  | Unsupported Media Type — check the `Content-type` header. |
| 503  | Service Unavailable — a dependent internal service is down. Body: `{ "code": 503, "message": "..." }`. |
| 596  | Service Not Found — `<h1>596 Service Not Found</h1>`. Verify the HTTP verb matches the documented endpoint. |

## Notifications

### TLS 1.0 Deprecation

Brivo dropped support for API calls over TLS 1.0 connections on January 31,
2019. TLS 1.2 or newer is required.

TLS is the protocol that encrypts HTTPS connections. TLS 1.0 was standardized
[in 1999](https://www.ietf.org/rfc/rfc2246.txt) and is vulnerable to attacks
such as [BEAST](https://blog.qualys.com/ssllabs/2013/09/10/is-beast-still-a-threat)
and [POODLE](https://blog.qualys.com/ssllabs/2014/12/08/poodle-bites-tls).
TLS 1.1 [followed in 2006](https://www.ietf.org/rfc/rfc4346.txt) and mitigated
BEAST, but major browsers
[jumped directly to TLS 1.2](https://bugzilla.mozilla.org/show_bug.cgi?id=422232#c2)
(codified [in 2008](https://www.ietf.org/rfc/rfc5246.txt)).

Standards bodies including
[PCI SSC](https://www.pcisecuritystandards.org/) and
[NIST](https://www.nist.gov/) recommend disabling TLS 1.0/1.1.
[PCI](https://blog.pcisecuritystandards.org/migrating-from-ssl-and-early-tls)
requires TLS 1.1 minimum, with TLS 1.2 recommended;
[NIST](https://csrc.nist.gov/publications/detail/sp/800-52/rev-2/draft)
requires TLS 1.2.

#### Common environments to upgrade

- **Internet Explorer 10 or earlier** — TLS 1.2 is enabled by default starting
  in IE 11 on Windows 7 / Server 2008 R2.
- **Java 6 or 7** — install a current Java Cryptography Extension (JCE) and a
  recent JRE update. TLS 1.2 support landed in JRE
  [1.7.0_131-b12](http://www.oracle.com/technetwork/java/javase/7u131-relnotes-3338543.html).
- **Java 8** — TLS 1.2 default; nothing to do.
- **Windows OS** — TLS 1.1 / 1.2 default in Windows 8.1 / Server 2012 R2 and
  later. Earlier versions need a Microsoft update.
- **.NET Framework** — see the
  [.NET TLS best practices guide](https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls).
- **PowerShell** — see the
  [PowerShell.org discussion on TLS 1.2](https://powershell.org/forums/topic/is-it-possible-to-enable-tls-1-2-as-default-in-powershell/).
- **curl** — relies on the underlying crypto library (OpenSSL, NSS, etc.).
  TLS 1.2 support
  [landed in OpenSSL 1.0.1](https://github.com/openssl/openssl/blob/OpenSSL_1_0_1-stable/CHANGES)
  in March 2012.
- **Linux OpenSSL** — Ubuntu 12.04+, RHEL/CentOS 6+, Debian 7+ are required.
  Older distributions must be upgraded.
- **OS X OpenSSL** — upgrade via Homebrew (`brew install openssl`), then
  reinstall any language runtimes that link to it (Python, Ruby, PHP, Node).
- **Windows OpenSSL** — your development environment provides OpenSSL.
  Upgrade by upgrading the runtime
  ([Python](https://python-security.readthedocs.io/ssl.html#openssl-versions),
  [Ruby](https://bundler.io/v1.16/guides/rubygems_tls_ssl_troubleshooting_guide.html#windows-installed-with-ruby-installer),
  [PHP](http://php.net/manual/en/openssl.installation.php),
  [Node](https://nodejs.org/en/)).

Questions or concerns: <BrivoApi@Brivo.com>.
