# External promotions

Besides the promotion types that are supported out-of-the-box ([buy X pay Y](https://app.gitbook.com/s/RWJeylueWkzLadK710XZ/buy_x_pay_y_promotions), [fixed amount](https://docs.commercelayer.io/developers/v/api-reference/fixed_amount_promotions),  [fixed price](https://docs.commercelayer.io/developers/v/api-reference/fixed_price_promotions), [free gift](https://docs.commercelayer.io/developers/v/api-reference/free_gift_promotions), [free shipping](https://docs.commercelayer.io/developers/v/api-reference/free_shipping_promotions), and [percentage discount](https://docs.commercelayer.io/developers/v/api-reference/percentage_discount_promotions)) Commerce Layer lets you integrate any kind of promotional engine as an [external promotion](https://docs.commercelayer.io/developers/v/api-reference/external_promotions).

When an external promotion activates — i.e. its activation rules are met — Commerce Layer triggers a `POST` request to the `promotion_url` endpoint, sending the order payload (including its line items) in the request body.

### Request

The request payload is a [JSON:API](https://jsonapi.org/) compliant object you can query to perform your own computation. Aside from the **target resource** — `orders` in the specific case — some **relationships** are also included to avoid useless API roundtrips:

* `market`
* `customer`
* `customer.tags`
* `line_items`
* `line_items.item`
* `shipping_address`
* `billing_address`

<pre class="language-json"><code class="lang-json">{
  "data": {
    "id": "wBXVhKzrnq",
<strong>    "type": "orders",
</strong>    "links": { ... },
    "attributes": { ... },
    "relationships": { ... },
    "meta": { ... }
  },
  "included": [
    {
      "id": "DvlGRmhdgX",
<strong>      "type": "markets",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "bGvCXzYgNB",
<strong>      "type": "customers",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "YawgnfmKad",
<strong>      "type": "tags",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "kdPgtRXOKL",
<strong>      "type": "line_items",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "XGZwpOSrWL",
<strong>      "type": "skus",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    }
    {
      "id": "BgnguJvXmb",
<strong>      "type": "addresses",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "AlrkugwyVW",
<strong>      "type": "addresses",
</strong>      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    { ... }
  ]
}
</code></pre>

{% hint style="info" %}
If needed, you can [overwrite the default list](https://docs.commercelayer.io/core/external-resources/..#custom-include-list) of included resources by passing your custom list of order's relationships within the `external_includes` attribute of the external promotion at creation or update time (nested relationships are allowed using dot notation).
{% endhint %}

{% hint style="warning" %}
In case the call to your external endpoint goes timeout, responds with an internal server error, or is blocked by the open [circuit breaker](https://docs.commercelayer.io/core/external-resources/..#circuit-breaker), the API keeps responding with a `200 OK` status code. No promotion is added to the order and the circuit breaker internal counter (if closed) is incremented.
{% endhint %}

### Response

Your service response (or error) must match the format described in the examples below.

#### Applying the discount to all taxable items of the order

{% tabs %}
{% tab title="Response" %}
The successful response must be a JSON object, returning at least the discount computed by the external logic. If needed, you can optionally enrich the response with some additional information, notification messages, and metadata:

<pre class="language-json"><code class="lang-json">{
  "success": true,
  "data": {
    "name": "My External Promotion",
<strong>    "discount_cents": 1500,
</strong>    "messages": [ ... ],
    "metadata": {
      "foo": "bar"
    }
  }
}
</code></pre>

{% endtab %}

{% tab title="Error" %}
On error, you must respond with an HTTP code >= `400`. The response body must be a JSON object containing any other relevant error code and message:

<pre class="language-json"><code class="lang-json">{
  "success": false,
  "error": {
<strong>    "code": "YOUR-ERROR-CODE",
</strong><strong>    "message": "Your error message"
</strong>  }
}
</code></pre>

{% endtab %}
{% endtabs %}

#### Applying the discount to specific taxable items

{% tabs %}
{% tab title="Response" %}
The successful response must be a JSON object, returning at least the discount computed by the external logic to be applied to the specified line items. If needed, you can optionally enrich the response with some additional information, notification messages, and metadata:

<pre class="language-json"><code class="lang-json">{
  "success": true,
  "data": {
    "name": "My External Promotion",
<strong>    "discount_cents": 1500,
</strong>    "line_items": [
      {
<strong>        "id": "kxnXtEaGxo"
</strong>      },
      {
<strong>        "id": "kXBqtrgARW"
</strong>      }
    ],
    "messages": [ ... ],
    "metadata": {
      "foo": "bar"
    }
  }
}
</code></pre>

{% endtab %}

{% tab title="Error" %}
On error, you must respond with an HTTP code >= `400`. The response body must be a JSON object containing any other relevant error code and message:

<pre class="language-json"><code class="lang-json">{
  "success": false,
  "error": {
<strong>    "code": "YOUR-ERROR-CODE",
</strong><strong>    "message": "Your error message"
</strong>  }
}
</code></pre>

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Be aware that specifying the line items in the response will overwrite both the associated SKU list (if any) and the [taxable items](https://app.gitbook.com/s/RWJeylueWkzLadK710XZ/orders#taxable-items) options (e.g. you can distribute the discount on a purchased gift card also if the `gift_card_taxable` is `false`). In case any of the provided line item IDs is not associated with the order, no promotion is applied.
{% endhint %}

#### Applying a specific discount to specific taxable items

{% tabs %}
{% tab title="Response" %}
The successful response must be a JSON object, returning at least a specific discount computed by the external logic to be applied to the specified line items. If needed, you can optionally enrich the response with some additional information, notification messages, and metadata:

<pre class="language-json"><code class="lang-json">{
  "success": true,
  "data": {
    "name": "My External Promotion",
    "line_items": [
      {
<strong>        "id": "kxnXtEaGxo",
</strong><strong>        "discount_cents": 1000
</strong>      },
      {
<strong>        "id": "kXBqtrgARW",
</strong><strong>        "discount_cents": 500
</strong>      }
    ],
    "messages": [ ... ],
    "metadata": {
      "foo": "bar"
    }
  }
}
</code></pre>

{% endtab %}

{% tab title="Error" %}
On error, you must respond with an HTTP code >= `400`. The response body must be a JSON object containing any other relevant error code and message:

<pre class="language-json"><code class="lang-json">{
  "success": false,
  "error": {
<strong>    "code": "YOUR-ERROR-CODE",
</strong><strong>    "message": "Your error message"
</strong>  }
}
</code></pre>

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
If you are going to specify the `discount_cents` key for the single line items, make sure to do it for all of the elements of the `line_items` array. Keep in mind that the promotion amount is computed and distributed only for the line items whose IDs are listed in the array with a `discount_cents` key specified.  In this case, avoid adding the `discount_cents` key at order the level because it's ignored if there is any line item with a specific `discount_cents` key set.
{% endhint %}

{% hint style="info" %}
If you specify the `messages` key a [notification](https://docs.commercelayer.io/core/external-resources/..#notifications) with the related information is automatically attached to the associated order.
{% endhint %}

### Security

When you create a new external promotion, a **shared secret** is generated. We recommend verifying the callback authenticity by signing the payload with that shared secret and comparing the result with the callback signature header.

{% content-ref url="../callbacks-security" %}
[callbacks-security](https://docs.commercelayer.io/core/callbacks-security)
{% endcontent-ref %}

####
