Configuration
All configuration can be set via the published config file or environment variables.
Header Names
Idempotency Key Header
The header clients use to send their idempotency key:
'main_header_name' => env('IDEMPOTENCY_MAIN_HEADER', 'Idempotency-Key'),
Replayed Response Header
The header added to responses that are served from cache:
'repeated_header_name' => env('IDEMPOTENCY_REPEATED_HEADER', 'Idempotent-Replayed'),
Expiration Time
How long cached responses are stored, in minutes. After this time, the idempotency key can be reused:
'expiration_time' => env('IDEMPOTENCY_EXPIRATION_TIME', 360), // 6 hours
HTTP Methods
Which HTTP methods the middleware applies to. Requests using other methods are passed through without idempotency handling:
'http_methods' => ['POST', 'PUT', 'PATCH', 'DELETE'],
To limit it to only POST requests:
'http_methods' => ['POST'],
Environment Variables
You can configure everything via .env without publishing the config file:
IDEMPOTENCY_MAIN_HEADER=Idempotency-Key
IDEMPOTENCY_REPEATED_HEADER=Idempotent-Replayed
IDEMPOTENCY_EXPIRATION_TIME=360
Scoping the Cache (multi-tenant apps)
By default the middleware stores each response under the client's idempotency key alone, a single global namespace. In a multi-tenant API that means two tenants (or the same tenant in live vs. test) that happen to send the same key would collide.
To prevent this, bind your own IdempotencyScope. The middleware asks it for a scope string and partitions the cache by it, so the same key is isolated per scope:
use BlueBeetle\IdempotencyMiddleware\Contracts\IdempotencyScope;
use Illuminate\Http\Request;
final class TenantIdempotencyScope implements IdempotencyScope
{
public function resolve(Request $request): string
{
// Return a stable identifier for the authenticated tenant/environment.
// The middleware runs after your auth middleware, so the user is available.
$user = $request->user();
return $user === null ? '' : "{$user->tenant_id}:{$user->environment}";
}
}
Bind it in a service provider:
$this->app->bind(IdempotencyScope::class, TenantIdempotencyScope::class);
Return an empty string for the global scope. If you don't bind a resolver, the default (GlobalIdempotencyScope) keeps the original single-namespace behaviour.
Cache Driver
The middleware uses Laravel's default cache driver. To use a specific driver for idempotency, configure a dedicated cache store in config/cache.php and set it as default, or use a tagged cache approach in your application.
For production, a persistent cache driver like Redis or Memcached is recommended to ensure idempotency keys survive across deployments.