> ## Documentation Index
> Fetch the complete documentation index at: https://v0.x-docs.novu.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotent Requests

> Retry API requests fearlessly, ensuring the operation performs just once!

Our platform has a seamless integration of optional [idempotent](https://en.wikipedia.org/wiki/Idempotence) requests for `POST` and `PATCH` operations.
This integration closely adheres to the [Idempotency-key ietf draft](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/).
These idempotent operations deliver the same result regardless of the number of requests made, using an idempotent key.

Leveraging this feature guarantees the safety and reliability of your requests, especially in cases where network or communication issues might inject uncertainty into request outcomes.
Idempotent requests are <b>critical</b> for workflows that require the insurance of only one successful delivery request.

<Note>
  This feature was introduced in version 0.22.0 and is fully operable from 0.24.0 and beyond. Currently, the Idempotency headers are not enabled on the cloud platform but are available for self-hosting.
</Note>

<Warning>
  Please be aware that idempotency might not be supported in all community SDKs.
</Warning>

## How to Perform an Idempotent Request with SDKs

Supporting SDKS will implement Idempotency for you as long as you have retries turned on.
Not only is this seamless but it also ensures that no event request is dropped provided that your system stays up.

<Note>
  Idempotency is only implemented for /events/trigger endpoint.
</Note>

### Idempotent request with api

To make an idempotent request, simply include the `Idempotency-Key: <key>` in your request header.
This key should be a unique client-generated value, boasting enough entropy to prevent collisions;
we recommend a collision-resistant Unique Identifier, such as UUIDv7, CUID, or ULID, as your idempotency keys.

## Understanding Response Replay

Our API stores the status code and response body from your initial idempotent request.
This way if an identical idempotency key appears in a later request,
the API will replay the saved response from the original request - even if it involves client or server error responses.

In the event of a response replay, the response will carry an `Idempotency-Replay: true` header.

## Idempotent Request Responces

Here are some unique pieces constraints you should be aware of especially if you are not using the sdk:

1. A `<key>` exceeding 255 characters will trigger a `400 Bad Request` error.
2. An inconsistent request body compared to the initial request will return a `422 Unprocessable Entity` error.
3. A ongoing processing of the initial request that hasn't yet responded will result in a `409 Conflict` error. This will include a `Retry-After` header.

<Note>
  Results will <b>not</b> saved if an API request is invalid.
</Note>

## Expiring Keys

All idempotency keys are ensured to be automatically removed once they are 24 hours and 1 minute old.
After this period, the reused key will be treated as a new request and response.

## Practical Examples

### Example 1 - No idempotency

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    import { Novu } from '@novu/node';

    const novu = new Novu("<NOVU_API_KEY>");

    await novu.trigger("<WORKFLOW_TRIGGER_IDENTIFIER>", {
      to: {
        subscriberId: "<UNIQUE_SUBSCRIBER_IDENTIFIER>",
      },
      payload: {
        name: "Hello World",
      },
      actor: "actorId",
      tenant: "tenantIdentifier",
    });
    ```
  </Tab>
</Tabs>

### Example 2 - Idempotency Active

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    import { Novu } from '@novu/node';

    const novu = new Novu("<NOVU_API_KEY>", {
        retryConfig: {
            retryMax: 5
        }
    });

    // This request is now idempotent
    await novu.trigger("<WORKFLOW_TRIGGER_IDENTIFIER>", {
      to: {
        subscriberId: "<UNIQUE_SUBSCRIBER_IDENTIFIER>",
      },
      payload: {
        name: "Hello World",
      },
      actor: "actorId",
      tenant: "tenantIdentifier",
    });
    ```
  </Tab>
</Tabs>

## References

* [https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/eo](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/eo)
