# Introduction

Pliro is an all-in-one tool helping independent publishers run and grow their subscription business.

We're improving the documentation we go along. Reach out to <hej@pliro.co> if you have any questions.


# Pliro Site

Your Pliro Site is where your customers checkout and manage their account.


# The customer account page

This page allows your customers to update their details and manage their subscription. If a customer isn't signed in when attempting to visit this page, they will be prompted to do so.

### Link target

`https://your-pliro-site-domain`


# The offers page

This page lists your publication's offers with prices, your free offering, and links to subscribe. You can use this page as your pricing page.

### Link target

`https://your-pliro-site-domain/offers`

### Optional query parameters

`continue_url`\
If provided, a link to this URL will be presented on the subscription confirmation page if the customer signs up for any of your offers or your free offering. To prevent abuse, Pliro will only present links to allowlisted hosts. Email [calle@pliro.co](mailto:calle@luks.in) and I will help you with this.


# The subscribe page

This page show the sign up form and lets the visitor pick from your publication's listed offers. If a customer isn't signed in when attempting to visit this page, they will be prompted to create an account first. If a customer is signed and they already have a subscription, these pages will show a message or allow the customer to upgrade their subscription if applicable.

### Link target

`https://your-pliro-site-domain/subscribe`

### Optional query parameters

`continue_url`\
If provided, a link to this URL will be presented on the confirmation page shown after the customer has subscribed to an offer. If a customer is signed in and already have a subscription, they will be automatically redirected to the `continue_url`. To prevent abuse, Pliro will only present links or redirect to allowlisted hosts. Email [calle@pliro.co](mailto:calle@luks.in) and I will be happy to help you with this.

`email`\
If provided, the subscribe flow will be initiated with the email and the customer will be prompted to confirm their email with the code that has been delivered to their inbox. Example use: Allow the customer to provide their email directly in the paywall and bypass that step in the signup flow.


# Offer pages

These pages show information about one of your publication's offers with a form to subscribe. If a customer is signed in and they already have a subscription, these pages will show a message or a link to upgrade the customer's subscription if applicable.

### Link target

`https://your-pliro-site-domain/offers/:offer`

These URLs are shown for your offers in the Pliro Dashboard.

### Optional query parameters

`continue_url`\
If provided, a link to this URL will be presented on the confirmation page shown after the customer signs up via an offer. If a customer is signed in and already have a subscription, they will be automatically redirected to the `continue_url`. To prevent abuse, Pliro will only present links or redirect to allowlisted hosts. Email [calle@pliro.co](mailto:calle@luks.in) and I will be happy to help you with this.


# Offer subscribe pages

These pages show the sign up form for one of your publication's offers. If a customer isn't signed in when attempting to visit this page, they will be prompted to create an account first. If a customer is signed and they already have a subscription, these pages will show a message or allow the customer to upgrade their subscription if applicable.

### Link target

`https://your-pliro-site-domain/offers/:offer/subscribe`

These URLs are shown for your offers in the Pliro Dashboard.

### Optional query parameters

`continue_url`\
If provided, a link to this URL will be presented on the confirmation page shown after the customer has subscribed to the offer. If a customer is signed in and already have a subscription, they will be automatically redirected to the `continue_url`. To prevent abuse, Pliro will only present links or redirect to allowlisted hosts. Email [calle@pliro.co](mailto:calle@luks.in) and I will be happy to help you with this.

`email`\
If provided, the subscribe flow will be initiated with the email and the customer will be prompted to confirm their email with the code that has been delivered to their inbox. Example use: Allow the customer to provide their email directly in the paywall and bypass that step in the signup flow.


# The free offering page

This page shows information about your publication's free offering with a link to subscribe. If a customer is signed in, these pages will show a message instead of a subscribe link.

### Link target

`https://your-pliro-site-domain/free_offering`

This URL is shown for your free offering in the Pliro Dashboard.

### Optional query parameters

`continue_url`\
If provided, a link to this URL will be presented on the confirmation page shown after the customer has signed up for your free offering. If a customer is signed in, they will be automatically redirected to the `continue_url`. To prevent abuse, Pliro will only present links or redirect to allowlisted hosts. Email [calle@pliro.co](mailto:calle@luks.in) and I will be happy to help you with this.


# The free offering subscribe page

This page shows the sign up form for your publication's free offering. If a customer is signed in and they already have a subscription, the customer will be redirected to the free offering page and shown a message saying they are already subscribed.

### Link target

`https://your-pliro-site-domain/free_offering/subscribe`

This URL is shown for your free offering in the Pliro Dashboard.

### Optional query parameters

`continue_url`\
If provided, a link to this URL will be presented on the confirmation page shown after the customer has signed up for your free offering. If a customer is signed in, they will be automatically redirected to the `continue_url`. To prevent abuse, Pliro will only present links or redirect to allowlisted hosts. Email [calle@pliro.co](mailto:calle@luks.in) and I will be happy to help you with this.

`email`\
If provided, the subscribe flow will be initiated with the email and the customer will be prompted to confirm their email with the code that has been delivered to their inbox. Example use: Allow the customer to provide their email directly in the paywall and bypass that step in the signup flow.


# Pliro Connect

Allow customers to sign into your website and manage their access to protected content.

Pliro Connect provides an authentication solution that makes it possible to sign customers into your website, e-magazine, app or other service and grant them access to protected content. The solution is based on [OpenID Connect](https://openid.net/connect/) and [OAuth 2.0](https://oauth.net/2/).

Here's an overview of how it works:

1. When a customer clicks the sign in button on your website, you redirect them to Pliro's sign in-flow.
2. After completing the flow, they are redirected back to your website with a code you can use to request an **ID token** and an **access token**. The ID token contains information about the signed in customer in the form of a [JSON Web Token](https://www.rfc-editor.org/rfc/rfc7519). The access token can be used to retrieve updated information about the customer.
3. You sign the customer into your website and store the tokens in their session.
4. When a customer clicks the sign out button on your website, you end their session. If you'd like, you can then redirect them to Pliro to end their Pliro session as well.
5. When a customer clicks the sign out button on their account page in Pliro, you can optionally receive a notification over HTTP, and end their session on your website.

We also provide [an example integration](/connect/example-integration) that demonstrates all of these flows.

## Prerequisites

To get started you'll need to create an OAuth application in Pliro. This can be done from the Pliro dashboard by navigating to "Settings > OAuth applications > New application". We recommend using [Pliro's test environment](/test-environment) while building out your integration. If you are working on your local machine you can set your application's Redirect URI to `http://localhost:3000/callback` or something similar.

After creating an OAuth application, the application's client ID and client secret will be shown. You'll need these credentials to authenticate your requests to Pliro.

## Supported application types

Pliro currently only supports OAuth applications that can securely store a client secret, i.e., server-side web apps. In the future, we hope to extend support to client-side web apps (SPAs) as well as mobile and native apps.

## Learn how to:

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Sign in customers</strong></td><td>Sign customers into your website and grant them access to protected content.</td><td><a href="/pages/qMvHHtuoYBs14kremIJx">/pages/qMvHHtuoYBs14kremIJx</a></td></tr><tr><td><strong>Update customer information</strong></td><td>Respond to customer actions within Pliro.</td><td><a href="/pages/bR1ksSnPwcqbJDpKCEvI">/pages/bR1ksSnPwcqbJDpKCEvI</a></td></tr><tr><td><strong>Silently re-authenticate customers</strong></td><td>Check if a customer is still signed into Pliro.</td><td><a href="/pages/wgTojJELYsGDNfNqHyzZ">/pages/wgTojJELYsGDNfNqHyzZ</a></td></tr><tr><td><strong>Sign customers out of Pliro</strong></td><td>Notify Pliro when signing customers out of your website.</td><td><a href="/pages/AIsy0FyffRyZf0nQA2fu">/pages/AIsy0FyffRyZf0nQA2fu</a></td></tr><tr><td><strong>Sign-out notifications</strong></td><td>Be notified when customers sign out of Pliro.</td><td><a href="/pages/x7WNfgijAAEt4i6oYVpJ">/pages/x7WNfgijAAEt4i6oYVpJ</a></td></tr></tbody></table>


# Sign in customers

Sign customers into your website and grant them access to protected content.

Pliro supports signing customers into your website using [the OpenID Connect Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth):

{% @mermaid/diagram content="sequenceDiagram
Customer->>Your website: Clicks the sign in button
Your website->>Your website: Generates authorization URI including a redirect URI
Your website-->>Customer: Redirects to Pliro's authorization endpoint
Customer->>Pliro: Requests the authorization endpoint
Pliro-->>Customer: Shows the Pliro sign in screen
Customer->>Pliro: Signs in
Pliro-->>Customer: Redirects to the provided redirect URI
Customer->>Your website: Requests the redirect URI
Your website->>Pliro: Requests access and ID tokens
Pliro-->>Your website: Responds with access and ID tokens
Your website->>Your website: Signs customer in and stores tokens
Your website-->>Customer: Welcomes signed in customer" %}

Here's how it works step-by-step:

## Step 1: Request an authorization code

When the customer clicks the sign in button on your website you'll need to redirect them to Pliro's authorization endpoint:

```
HTTP/1.1 302 Found
Location: https://example-publication.plirotest.page/oauth/authorize?
  response_type=code&
  scope=openid%20email%20profile&
  client_id=example-client-id&
  redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
```

This endpoint requires the following query parameters:

* `response_type`: Must be set to `code`.
* `scope`: A space-separated list of requested scopes. Must include `openid`. Can optionally include `email` to request that the customer's email is included in ID tokens, and `profile` to request the inclusion of the customer's name and subscription plan. Example: `openid email profile`.
* `client_id`: Must be set to the client ID shown for your OAuth application in the Pliro dashboard.
* `redirect_uri`: Must be set to the redirect URI you have specified for your OAuth application in the Pliro dashboard. The provided URI can optionally include query parameters not included in the registered redirect URI.

After you have redirected the customer to this endpoint, Pliro will prompt the customer to sign in. When the customer has signed in, Pliro will redirect them to the provided `redirect_uri` with an authorization code in the `code` query parameter. For example:

```
HTTP/1.1 302 Found
Location: https://example.com/callback?code=example-code
```

The authorization endpoint also accepts the following optional parameters:

* `state`: Recommended. If set, a `state` query parameter with the same value will be added to the `redirect_uri` before redirecting the customer back to your website. We recommend storing this value in the customer's session in a way that can't be tampered with before redirecting them to Pliro, and then checking that the `state` parameter in the request to the redirect URI matches the one stored in the session. This provides [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection.
* `prompt`: When set to `none`, Pliro won't prompt the customer to sign in. See the section [Silent re-authentication](#silent-re-authentication) below for more information on this.

### Error handling

If the authorization request fails due to an invalid `redirect_uri` or `client_id`, the customer will not be redirected to the `redirect_uri`.

In case of other errors, the customer will be redirected to the `redirect_ur` with an error code in the `error` query parameter. For example:

```
HTTP/1.1 302 Found
Location: https://example.com/callback?error=invalid_request
```

Pliro can return the following error codes:

* `invalid_request`: The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.
* `unauthorized_client`: The client is not authorized to request an authorization code using this method.
* `unsupported_response_type`: The server does not support obtaining an authorization code using this method.
* `invalid_scope`: The requested scope is invalid, unknown, or malformed.
* `server_error`: The server encountered an unexpected condition that prevented it from fulfilling the request.
* `login_required`: The customer needs to sign in. This error is returned when the `prompt` parameter is set to `none` and the customer is currently signed out of Pliro.

## Step 2: Request access and ID tokens

When your website processes the request to the `redirect_uri` (and has verified its authenticity using the `state` parameter), it can exchange the `code` for access and ID tokens by making a request to the token endpoint:

```
POST /oauth/token HTTP/1.1
Host: example-publication.plirotest.page
Content-Type: application/x-www-form-urlencoded
Authorization: Basic WU9VUl9DTElFTlRfSUQ6WU9VUl9DTElFTlRfU0VDUkVU

grant_type=authorization_code&
code=example-code&
redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
```

The request needs to include [Basic authentication](https://datatracker.ietf.org/doc/html/rfc7617) using your application's client ID as the username and client secret as the password.

The successful response includes access and ID tokens:

```
HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "example-access-token",
  "token_type": "Bearer",
  "expires_in":7200,
  "scope": "openid email profile",
  "created_at": 1679395531,
  "id_token": "example-id-token"
}
```

### Error handling

If the token request fails, the server responds with an HTTP 400 or 401 status code and includes an error code in the response body:

```
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
 "error": "invalid_request"
}
```

* `invalid_request`: The request is missing a required parameter, includes an unsupported parameter value (other than grant type), repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed.
* `invalid_client`: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).
* `invalid_grant`: The provided authorization grant (e.g., authorization code) is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
* `unauthorized_client`: The authenticated client is not authorized to use this authorization grant type.
* `unsupported_grant_type`: The authorization grant type is not supported by the authorization server.

## Step 3: Sign the customer in and store their tokens

You can now sign the customer into your website (e.g., by setting a session cookie). You should also store the following information in their session:

* Their access token: This is required to [retrieve updated customer information](#retrieve-updated-customer-information).
* Their id token: This is required to [sign customers out of Pliro](#sign-customers-out-of-pliro).

The ID token contains additional information, in the form of a [JSON Web Token](https://www.rfc-editor.org/rfc/rfc7519), that you may want to store separately:

* The customer's Pliro session ID (`sid`): This can be useful when [processing sign-out notifications](/connect/sign-out-notifications).
* The customer's email (if the `email` scope was included in the authorization request).
* The customer's name (if the `profile` scope was included in the authorization request).
* The slug for the plan that the customer subscribes to (if the `profile` scope was included in the authorization request). This can be useful when managing the customer's access to protected content.

To access this information, the ID token needs to be decoded. This can be done with one of [the many existing JWT libraries](https://jwt.io/libraries). Use [Pliro's signing keys](/connect/signing-keys) when verifying the token's signature.

## Step 4: Grant the customer access to protected content

Whenever the customer attempts to access a protected piece of content you can use the information stored in their session to authorize their access. At some point you may want to [update this information](/connect/update-customer-information).


# Update customer information

Respond to customer actions within Pliro.

When a customer signs into your website, you receive information about them in the form of an ID token. If a customer then changes their email or name, or purchase or cancel their subscription from within Pliro, the information received in the original ID token will be out of date. For the most part this is fine, but in some cases it can cause problems.

For example, if a customer signs into your website, purchases a subscription from within Pliro, and then tries to access a protected piece of content on your website, you run the risk of denying them access since you have yet to learn about their purchase.

In cases like this you'll want to retrieve updated information about the customer. This can be done with a request to the userinfo endpoint:

```
GET /oauth/userinfo HTTP/1.1
Host: example-publication.plirotest.page
Authorization: Bearer example-access-token
```

The request needs to include [Bearer authentication](https://datatracker.ietf.org/doc/html/rfc6750) using the customer's access token.

The successful response includes the customer's email, name, and plan if the corresponding scopes where requested when creating the access token:

```
HTTP/1.1 200 OK
Content-Type: application/json

{
  "sub": "cus_1BxEm4QKh4Rb5v6Qvu2THx",
  "email": "customer@example.com",
  "name": "Example Name",
  "plan": "example-plan",
  "plan_slugs": ["example-plan"]
}
```

## Error handling

If the request fails due to an invalid access token, the server responds with an HTTP 401 status code and a `WWW-Authenticate` containing the `invalid_token` error code:

```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="Pliro", error="invalid_token", error_description="The access token is invalid"
```

When this happens, you might want to try [silently re-authenticating the customer](/connect/silent-re-authentication) before signing the them out of your website.


# Silent re-authentication

Check if a customer is still signed into Pliro.

Another way of retrieving updated information about the customer is to use the silent re-authentication flow. This is, in essence, the same flow described in [Sign in customers](/connect/sign-in-customers) but without presenting the sign in screen to the customer.

You activate the silent re-authentication flow by including a `prompt` query parameter with the value `none` in the [authorization request](/connect/sign-in-customers#step-1-request-an-authorization-code).

If the customer is already signed into Pliro they will immediately be redirected to the `redirect_uri` with an authorization code.

If they are not signed into Pliro, the customer will be redirected to the `redirect_uri` with an `error` parameter set to `login_required`. At this point, you should consider signing the customer out of your website.


# Sign customers out of Pliro

Notify Pliro when signing customers out of your website.

When a customer signs out of your website you may optionally sign them out of Pliro using [the OpenID Connect RP-Initiated Logout mechanism](https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RedirectionAfterLogout).

You do this by redirecting them to Pliro's end session endpoint:

```
HTTP/1.1 302 Found
Location: https://example-publication.plirotest.page/oauth/end_session?
  client_id=example-client-id&
  id_token_hint=example-id-token&
  post_logout_redirect_uri=https%3A%2F%2Fexample.com
```

This endpoint accepts the following query parameters:

* `client_id`: Should be set to the client ID shown for your OAuth application in the Pliro dashboard.
* `id_token_hint`: Should be set to the ID token received when the customer signed into your website.
* `post_logout_redirect_uri`: Should be set to a URI to redirect the customer to, after signing them out of Pliro. If set, this URI must match the URI that is registered for the OAuth application in the Pliro dashboard.

After the customer is signed out of Pliro, they are redirected to the `post_logout_redirect_uri`.

```
HTTP/1.1 302 Found
Location: https://example.com
```

To prevent abuse, a valid `id_token_hint` is required to automatically sign the customer out of Pliro. If `id_token_hint` is missing or invalid, the customer will have to confirm signing out.

To prevent [unvalidated redirects](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html), Pliro will only redirect the customer to the `post_logout_redirect_uri` if it corresponds to the post-logout redirect URI registered for the OAuth application identified by `client_id` or the `aud` claim in the provided `id_token_hint`.


# Sign-out notifications

Be notified when customers sign out of Pliro.

When a customer signs out of Pliro by clicking the sign out button on their account page, you may also want to sign them out of your website. This can be done using [the OpenID Connect Back-Channel Logout mechanism](https://openid.net/specs/openid-connect-backchannel-1_0.html):

If you register a back-channel logout URI for your OAuth application in the Pliro dashboard, Pliro will notify your website using an HTTP POST request when one of your customers sign out:

```
POST /backchannel_logout HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

logout_token=example-logout-token
```

To prevent abuse and to communicate which customer to sign out, the request includes a `logout_token` containing information about the customer in the form of a [JSON Web Token](https://www.rfc-editor.org/rfc/rfc7519). Before signing a customer out of your website you must decode and verify the logout token using the following steps:

1. Decode the logout token (e.g., using one of [the many existing JWT libraries](https://jwt.io/libraries)) and verify its signature using [Pliro's signing keys](/connect/signing-keys).
2. Check that the `iss` claim is equal to your Pliro page URL (e.g., `https://example-publication.plirotest.page`.
3. Check that the `aud` claim is equal to your OAuth application's client ID.
4. Check that the Unix timestamp in the `iat` (issued at) claim is in the past. You may also want to check that the token isn't too old (e.g., 5 minutes).
5. Check that the `sub` claim is present. This claim contains the customer's Pliro ID.
6. Check that the `sid` claim is present. This claim contains the customer's Pliro session ID.
7. Check that the `events` claim include the key `http://schemas.openid.net/event/backchannel-logout`.&#x20;
8. Check that there is no `nonce` claim.
9. Check that the token's `typ` header is set to `logout+jwt`.

If any of these checks fail, you should respond with an HTTP 400 status code and not process the logout request.

If the the checks pass you may proceed to end the session corresponding to the Pliro session ID in the `sid` claim and respond with an HTTP 200 or 204 status code.

If ending the session fails, you should respond with an HTTP 400 status code.


# Example integration

Access the code for a working example integration, and try it out live.

Besides the step-by-step guidance in these docs, we also provide an example integration demonstrating how to sign customers into the website of a fictional newspaper called "The Greenfield Times". You can try it out live on [greenfieldtimes.news](https://www.greenfieldtimes.news). The code is available on GitHub:

{% embed url="<https://github.com/pliroco/sign-in-with-pliro-example>" %}


# Signing keys

Verify the signatures of ID and logout tokens.

These keys can be used to verify the signatures of ID and logout tokens in the respective environment:

## Production environment

```
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWd7J/bJFIYaUCvKQw4Lflf196INB
rSUTs4kbH0nABCH5H6HutUp8Ya8n9uB7qCNIJGkOOv8eMzymi27G32tlNw==
-----END PUBLIC KEY-----
```

## Test environment

```
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHIW3cGt20S+1MCtq98d7Atz1mXoC
TaDapvonJvZbVHcmnmms52NfzNZo0FwSgKRUmgSmcXUmh6grLbx6ZQE2zw==
-----END PUBLIC KEY-----
```


# API

Access your publication's data in Pliro.

Pliro's API provides programmatic access to your publication's data in Pliro. The API accepts [form-encoded](https://en.wikipedia.org/wiki/URL_encoding#The_application/x-www-form-urlencoded_type) request bodies, returns [JSON-encoded](https://www.json.org) responses, and uses standard HTTP status codes, authentication, and verbs.

The API is served over [HTTPS](https://en.wikipedia.org/wiki/HTTPS) on `api.pliro.co`. We also provide a test version of the API on `api.plirotest.com` (operating in our isolated [test environment](/test-environment)) that you can use when building and testing out your integrations.


# Authentication

Learn how to authenticate your API requests.

Pliro uses API keys to authenticate requests. You can view and manage your API keys in the Pliro Dashboard.

{% hint style="warning" %}
Your API keys grant full access to your publication's data within Pliro, so be careful not to expose your keys publicly on GitHub or in client-side code. If you believe one of your API keys might have been exposed, you can revoke it and create a new one in Pliro Dashboard.
{% endhint %}

Authentication is performed using [HTTP Basic auth](http://en.wikipedia.org/wiki/Basic_access_authentication). Provide your API key as the Basic auth username value. You don't need to provide a password:

```sh
curl -u "$PLIRO_API_KEY:" $PLIRO_API_BASE_URL/2023-04-11/customers
```

All API requests must be made over [HTTPS](https://en.wikipedia.org/wiki/HTTPS). Requests made over plain HTTP will fail. Requests without authentication will also fail.


# Errors

Learn how to interpret API error responses.

Pliro's API uses standard HTTP status codes to indicate the success or failure of a request. Codes in the `200-299` range indicate success, codes in the `400-499` range indicate a client error (e.g., omitting a required parameter or not providing a valid API token), and codes in the `500-599` range indicate an error with Pliro's servers (these should be rare).

All error responses include an `error` object:

```shell-session
$ curl -G $PLIRO_API_BASE_URL/2023-04-11/customers \
  -u $PLIRO_API_KEY: \
  -d limit=0
{
  "error": {
    "code": "invalid_param",
    "message": "limit must be greater than or equal to 1 but was 0."
  }
}
```

## The error object

```json
{
  "code": "invalid_param",
  "message": "limit must be greater than or equal to 1 but was 0."
}
```

### Properties

`code` **string**\
A short string identifying the error. See [error codes](/api/errors/error-codes) for a full list of possible codes.

`message` **string**\
A message providing more details about the error.


# Error codes

Learn more about error codes and how to resolve them.

All [API errors](/api/errors) include a `code` to identify them. Below is a full list of possible error codes and information on how to resolve them.

#### `invalid_api_key`

The provided API key was invalid. You can create and obtain a valid one from the Pliro Dashboard.

#### `invalid_api_version`

The provided API version was invalid. Please use one of the [supported versions](/api/versioning#supported-versions).

#### `invalid_param`

The value of one of the provided request parameters was invalid. See the `message` property for more details.

#### `missing_api_key`

No API key was provided in the request. See [Authentication](/api/authentication) for how to authenticate your API requests.

#### `not_found`

The requested resource could not be found. See the `message` property for more details.

#### `revoked_api_key`

The provided API key has been revoked. Obtain a new API key from the Pliro Dashboard.


# Pagination

Learn how to retrieve all objects in a collection.

All endpoints that retrieve a collection of objects support cursor-based pagination. When you make a request to one of these endpoints, it will respond with the first page of objects in the collection. If the collection includes additional objects, the response will also include a `next_page` property that can be used to request the next page of objects.

<details>

<summary>Example pagination requests and responses</summary>

#### Requesting the first page

{% code title="GET /2023-04-11/customers" %}

```bash
curl -G $PLIRO_API_BASE_URL/2023-04-11/customers \
  -u "$PLIRO_API_KEY:" \
  -d limit=2
```

{% endcode %}

{% code title="Response" %}

```json
{
  "objects": [
    {
      "id": "cus_1BxEmDZEfaMhFQxkysiGtm",
      "email": "jane@example.com",
      "name": "Jane Doe",
      "newsletter_slugs": [],
      "plan_slug": null
    },
    {
      "id": "cus_1BxEm4QKh4Rb5v6Qvu2THx",
      "email": "john@example.com",
      "name": "John Doe",
      "newsletter_slugs": [],
      "plan_slug": null
    }
  ],
  "next_page": "Y3VzXzFCeEVtM1I1bjRQU1lVN3ZwZWFpY3E"
}
```

{% endcode %}

#### Requesting the second page

{% code title="GET /2023-04-11/customers" %}

```bash
curl -G $PLIRO_API_BASE_URL/2023-04-11/customers \
  -u "$PLIRO_API_KEY:" \
  -d page=Y3VzXzFCeEVtM1I1bjRQU1lVN3ZwZWFpY3E \
  -d limit=2
```

{% endcode %}

{% code title="Response" %}

```json
{
  "objects": [
    {
      "id": "cus_1BxEm3R5n4PSYU7vpeaicq",
      "email": "richard@example.com",
      "name": "Richard Roe",
      "newsletter_slugs": [],
      "plan_slug": null
    }
  ],
  "next_page": null
}
```

{% endcode %}

</details>

## Pagination parameters

`limit` **integer**\
The maximum number of objects to retrieve.

`page`  **string**\
Omit this parameter to request the first page. Provide the `next_page` value returned in a previous response to request the next page.

## The page object

```json
{
  "objects": [
    {
      "id": "cus_1BxEmDZEfaMhFQxkysiGtm",
      "email": "jane@example.com",
      "name": "Jane Doe",
      "newsletter_slugs": [],
      "plan_slug": null,
      "activation_url": null
    },
    {
      "id": "cus_1BxEm4QKh4Rb5v6Qvu2THx",
      "email": "john@example.com",
      "name": "John Doe",
      "newsletter_slugs": [],
      "plan_slug": null,
      "activation_url": null
    }
  ],
  "next_page": "Y3VzXzFCeEVtM1I1bjRQU1lVN3ZwZWFpY3E"
}
```

### Properties

`objects` **array**\
An array of objects in reverse chronological order.

`next_page` **optional string**\
A cursor for the next page of objects.


# Versioning

Learn more about Pliro's API versioning.

The Pliro API is versioned and all requests must include an explicit version as part of the endpoint URL. Version names are based on the date they were released. For example, version `2023-04-11` of the API was released on April 11, 2023.

Backwards-incompatible changes are made in new API versions. These changes include:

* Removing existing API endpoints.
* Removing or renaming request parameters or response object properties for existing endpoints.
* Adding required request parameters to existing endpoints or making previously optional parameters required.
* Changing the type of request parameters or response object properties for existing endpoints.
* Removing enum values from request parameters or response object properties for existing endpoints.
* Changing authentication or authorization requirements.

Backwards-compatible changes are made to all supported API versions. These changes include:

* Adding new API endpoints.
* Adding optional request parameters to existing endpoints.
* Adding optional request headers to existing endpoints.
* Adding new response object properties to existing endpoints.
* Changing the order of response object properties for existing endpoints.
* Adding new response headers to existing endpoints.
* Adding new enum values to request parameters or response object properties for existing endpoints.

## Supported versions

The API currently supports the following versions:

* `2023-04-11`


# Endpoint reference

Learn more about Pliro's API endpoints and how to use them.

This page lists all API endpoints sectioned by object type. You can set the following variables in your shell to allow copying and pasting the examples below:

```sh
PLIRO_API_BASE_URL=https://api.plirotest.com # Or api.pliro.co when working with production data.
PLIRO_API_KEY=example-api-key # View and manage API keys in the Pliro Dashboard.
```

## Members

Members replace customers as the representation of your publication's potential, current, and past subscribers.

### The member object

```json
{
  "id": "memb_1CUCgU9jUQcdovHnXd638m",
  "email": "jane@example.com",
  "name": "Jane Doe",
  "newsletter_slugs": ["example-newsletter"],
  "plan_slug": "example-plan"
}
```

#### Properties

`id` **string**\
A unique identifier for this member.

`email` **string**\
The member's email address.

`name` **nullable string**\
The members's full name.

`newsletter_slugs` **array of strings**\
An array of slugs for newsletters the member subscribes to.

`plan_slug` **nullable string**\
The slug of the plan the member has an active subscription for.

### List all members

{% code title="GET /2023-04-11/members" %}

```sh
curl -G $PLIRO_API_BASE_URL/2023-04-11/members \
  -u "$PLIRO_API_KEY:" \
  -d limit=2
```

{% endcode %}

{% code title="Response" %}

```json
{
  "objects": [
    {
      "id": "memb_1CUCgU9jUQcdovHnXd638m",
      "email": "jane@example.com",
      "name": "Jane Doe",
      "newsletter_slugs": ["example-newsletter"],
      "plan_slug": "example-plan"
    },
    {
      "id": "memb_1CUCgigLsKNJdFw3nmsrHk",
      "email": "john@example.com",
      "name": "John Doe",
      "newsletter_slugs": [],
      "plan_slug": null
    }
  ],
  "next_page": "Y3VzXzFCeEVtM1I1bjRQU1lVN3ZwZWFpY3E"
}
```

{% endcode %}

#### Parameters

`limit` **integer**\
The maximum number of members to retrieve. Can range from 1 to 100 and defaults to 10.

`page` **string**\
Omit this parameter to request the first page. Use the `next_page` value returned in a previous response to request the next page.

#### Returns

A [page](/api/pagination#the-page-object) of member objects or an [error](/api/errors).

## Customers

Customers represent your publication's potential, current, and past subscribers.

### The customer object

```json
{
  "id": "cus_1BxEmDZEfaMhFQxkysiGtm",
  "email": "jane@example.com",
  "name": "Jane Doe",
  "newsletter_slugs": ["example-newsletter"],
  "plan_slug": "example-plan",
  "activation_url": null
}
```

#### Properties

`id` **string**\
A unique identifier for this customer.

`email` **nullable string**\
The customer's email address.

`name` **nullable string**\
The customer's full name.

`newsletter_slugs` **array of strings**\
An array of slugs for newsletters the customer subscribe to.

`plan_slug` **nullable string**\
The slug of the plan the customer has an active subscription for.

`activation_url` **nullable string**\
A url to which the customer can be directed to activate their account.

### List all customers

{% code title="GET /2023-04-11/customers" %}

```sh
curl -G $PLIRO_API_BASE_URL/2023-04-11/customers \
  -u "$PLIRO_API_KEY:" \
  -d limit=2
```

{% endcode %}

{% code title="Response" %}

```json
{
  "objects": [
    {
      "id": "cus_1BxEmDZEfaMhFQxkysiGtm",
      "email": "jane@example.com",
      "name": "Jane Doe",
      "newsletter_slugs": ["example-newsletter"],
      "plan_slug": "example-plan",
      "activation_url": null
    },
    {
      "id": "cus_1BxEm4QKh4Rb5v6Qvu2THx",
      "email": "john@example.com",
      "name": "John Doe",
      "newsletter_slugs": [],
      "plan_slug": null,
      "activation_url": null
    }
  ],
  "next_page": "Y3VzXzFCeEVtM1I1bjRQU1lVN3ZwZWFpY3E"
}
```

{% endcode %}

#### Parameters

`limit` **integer**\
The maximum number of customers to retrieve. Can range from 1 to 100 and defaults to 10.

`page` **string**\
Omit this parameter to request the first page. Use the `next_page` value returned in a previous response to request the next page.

#### Returns

A [page](/api/pagination#the-page-object) of customer objects or an [error](/api/errors).

### Create a customer

{% code title="POST /2023-04-11/customers" %}

```bash
curl $PLIRO_API_BASE_URL/2023-04-11/customers \
  -u "$PLIRO_API_KEY:" \
  --data-urlencode email='jane@example.com'
```

{% endcode %}

{% code title="Response" %}

```json
{
  "id": "cus_1BxEmDZEfaMhFQxkysiGtm",
  "email": "jane@example.com",
  "name": null,
  "newsletter_slugs": [],
  "plan_slug": null,
  "activation_url": null
}
```

{% endcode %}

#### Parameters

`email` **string** <mark style="color:red;">Required conditionally</mark>\
The customer's email address. Required unless `activation_code` is provided.

`name` **string**\
The customer's full name.

`activation_code` **string**\
A code that can be used to add an email address to this customer via the customer activation flow. Must be unique and at least eight characters long.

#### Returns

A [customer object](#the-customer-object) or an [error](/api/errors).

## Subscriptions

Subscriptions allow charging customers on a recurring basis.

### The subscription object

```json
{
  "id": "sub_1CAYdVkJ4hgBkAUrugKwaj",
  "status": "active",
  "customer_id": "cus_1BxEmDZEfaMhFQxkysiGtm",
  "plan_id": "plan_1CAYdcXnjeh3QDz5MZVPVu",
  "price_id": "price_1CAYdeEnWTYzneWDJxYb6A",
  "complimentary": false
}
```

#### Properties

`id` **string**\
A unique identifier for this subscription.

`status` **string**\
Possible values are `active` and `canceled`.

`customer_id` **string**\
The ID of the customer who owns this subscription.

`plan_id` **string**\
The ID of the plan that this subscription is for.

`price_id` **nullable string**\
The ID of the price that dictates the cost and recurrence interval of this subscription.

`complimentary` **boolean**\
Whether or not this subscription is provided to the customer free of charge. If `complimentary` is  `true`, then `price_id` will be `null`.

### Create a subscription

{% code title="POST /2023-04-11/subscriptions" %}

```bash
curl $PLIRO_API_BASE_URL/2023-04-11/subscriptions \
  -u "$PLIRO_API_KEY:" \
  -d customer_id=cus_1BxEmDZEfaMhFQxkysiGtm \
  -d plan_id=plan_1CAYdcXnjeh3QDz5MZVPVu \
  -d price_id=price_1CAYdeEnWTYzneWDJxYb6A
```

{% endcode %}

{% code title="Response" %}

```json
{
  "id": "sub_1CAYdVkJ4hgBkAUrugKwaj",
  "status": "active",
  "customer_id": "cus_1BxEmDZEfaMhFQxkysiGtm",
  "plan_id": "plan_1CAYdcXnjeh3QDz5MZVPVu",
  "price_id": "price_1CAYdeEnWTYzneWDJxYb6A",
  "complimentary": false
}
```

{% endcode %}

#### Parameters

`customer_id` **string** <mark style="color:red;">Required</mark>\
The ID of the customer who should own this subscription.

`plan_id` **string** <mark style="color:red;">Required</mark>\
The ID of the plan that this subscription should be for.

`price_id` **string** <mark style="color:red;">Required conditionally</mark>\
The ID of the price that should dictate the cost and recurrence interval of this subscription. Required unless `complimentary` is `true`.

`complimentary` **boolean**\
Whether or not this subscription should be provided to the customer free of charge. This parameter defaults to `false`. If `complimentary` is  `true`, then `price_id` must be `null`.

#### Returns

A [subscription object](#the-subscription-object) or an [error](/api/errors).

### Cancel a subscription

{% code title="POST /2023-04-11/subscriptions/:id/cancel" %}

```bash
curl $PLIRO_API_BASE_URL/2023-04-11/subscriptions/sub_1CAYdVkJ4hgBkAUrugKwaj/cancel \
  -u "$PLIRO_API_KEY:"
```

{% endcode %}

{% code title="Response" %}

```json
{
  "id": "sub_1CAYdVkJ4hgBkAUrugKwaj",
  "status": "canceled",
  "customer_id": "cus_1BxEmDZEfaMhFQxkysiGtm",
  "plan_id": "plan_1CAYdcXnjeh3QDz5MZVPVu",
  "price_id": "price_1CAYdeEnWTYzneWDJxYb6A",
  "complimentary": false
}
```

{% endcode %}

#### Parameters

No parameters.

#### Returns

A [subscription object](#the-subscription-object) or an [error](/api/errors).


# Test environment

Build and test your integration in a safe and production-like environment.

Pliro provides an isolated test environment that you can use when building and testing out your custom integrations. This eliminates the risk of affecting production data or incurring costs.

Access to the test environment is currently provided by invitation. Email <calle@pliro.co> and I'll help you get set up!


