# Create a subscription checkout link

Source: https://ethicsportal.eu/developers/api/operations/createcheckoutlink/

POST

/organizations/{organization\_id}/subscriptions/checkout.json

```
const url = 'https://secure.ethicsportal.eu/organizations/example/subscriptions/checkout.json';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"subscription":{"interval":"year"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```
curl --request POST \
  --url https://secure.ethicsportal.eu/organizations/example/subscriptions/checkout.json \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{ "subscription": { "interval": "year" } }'
```

* EthicsPortal application

Return a Stripe Checkout link for the monthly or yearly plan. A person opens it and pays; Stripe handles strong customer authentication, tax and VAT ID collection. Access starts when Stripe confirms the payment, whether or not the payer is signed in to EthicsPortal. Send an Idempotency-Key so a retry returns the same link. A personal token also needs its user to be a current administrator.

## Authorizations

* **[personalToken](https://ethicsportal.eu/developers/api/#personaltoken)**  
billing:write
* **[organizationToken](https://ethicsportal.eu/developers/api/#organizationtoken)**  
billing:write

## Parameters

### Path Parameters

**organization\_id**

required

string

Obfuscated organisation ID returned by identity.

### Header Parameters

**Idempotency-Key**

string

\>= 1 characters <= 255 characters /^\[\\x21-\\x7e\]+$/

Recommended on every POST. Retrying with the same key and the same request within 24 hours returns the first response (with Idempotent-Replayed: true) instead of acting twice. Only successful responses and validation errors are stored; any other outcome releases the key. Reusing a key with a different request returns 409 idempotency\_key\_reused; a retry while the first request is still running returns 409 idempotency\_key\_in\_progress. Keys are scoped to the token.

## Request Bodyrequired

application/json

object

**subscription**

required

object

**interval**

required

string

Allowed values: month year

### Example 

```
{
  "subscription": {
    "interval": "year"
  }
}
```

## Responses

### 201

Checkout link created.

application/json

object

**url**

required

Stripe-hosted page. Give it to the person who pays or manages billing; it works for whoever holds it until it expires.

string format: uri

**expires\_at**

required

When the link stops working. Null for billing portal links, which expire shortly after creation; create one just before use.

string | null format: date-time

#### Example 

```
{
  "url": "https://checkout.stripe.com/c/pay/cs_live_example",
  "expires_at": "2026-11-10T10:00:00Z"
}
```

### 400

Invalid body

application/json

object

**error**

required

object

**code**

required

string

Allowed values: invalid\_token forbidden insufficient\_scope not\_found invalid\_request agreement\_required rate\_limited idempotency\_key\_reused idempotency\_key\_in\_progress not\_retryable channel\_inactive invitation\_unavailable already\_subscribed no\_subscription

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "invalid_request",
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```

### 401

Missing, unknown, expired or revoked token

application/json

object

**error**

required

object

**code**

required

string

Allowed values: invalid\_token forbidden insufficient\_scope not\_found invalid\_request agreement\_required rate\_limited idempotency\_key\_reused idempotency\_key\_in\_progress not\_retryable channel\_inactive invitation\_unavailable already\_subscribed no\_subscription

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "invalid_token",
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```

### 403

The token lacks the required scope (WWW-Authenticate names it), the user’s role does not allow the action, or an agreement must be accepted in the web application

application/json

object

**error**

required

object

**code**

required

string

Allowed values: invalid\_token forbidden insufficient\_scope not\_found invalid\_request agreement\_required rate\_limited idempotency\_key\_reused idempotency\_key\_in\_progress not\_retryable channel\_inactive invitation\_unavailable already\_subscribed no\_subscription

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "insufficient_scope",
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```

### 404

Organisation missing or inaccessible, API access suspended, or billing not configured

application/json

object

**error**

required

object

**code**

required

string

Allowed values: invalid\_token forbidden insufficient\_scope not\_found invalid\_request agreement\_required rate\_limited idempotency\_key\_reused idempotency\_key\_in\_progress not\_retryable channel\_inactive invitation\_unavailable already\_subscribed no\_subscription

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "not_found",
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```

### 409

The organisation already has a subscription (use the billing portal link), or an Idempotency-Key conflict (This Idempotency-Key was already used with a different request, or its first request is still running)

application/json

object

**error**

required

object

**code**

required

string

Allowed values: invalid\_token forbidden insufficient\_scope not\_found invalid\_request agreement\_required rate\_limited idempotency\_key\_reused idempotency\_key\_in\_progress not\_retryable channel\_inactive invitation\_unavailable already\_subscribed no\_subscription

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "already_subscribed",
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```

### 422

Unknown interval

application/json

object

**error**

required

object

**code**

required

Allowed value: validation\_failed

**fields**

required

Array<string>

\>= 1 items unique items

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "validation_failed",
    "fields": [
      "interval"
    ],
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```

### 429

Rate limited; retry after the Retry-After interval

application/json

object

**error**

required

object

**code**

required

string

Allowed values: invalid\_token forbidden insufficient\_scope not\_found invalid\_request agreement\_required rate\_limited idempotency\_key\_reused idempotency\_key\_in\_progress not\_retryable channel\_inactive invitation\_unavailable already\_subscribed no\_subscription

**feedback\_url**

required

Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.

string format: uri

#### Example 

```
{
  "error": {
    "code": "rate_limited",
    "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json"
  }
}
```
