Portal Community

Rate Limits Enforced

LimitDefaultConfiguration Key
Interactions per minute per user5RateLimiting.MaxPerMinutePerUser
Concurrent pending interactions per user10RateLimiting.MaxConcurrentPerUser
Burst allowance (above the per-minute limit)2RateLimiting.BurstAllowance

Configuration

// appsettings.json
{
  "EdgeInteract": {
    "RateLimiting": {
      "MaxPerMinutePerUser": 5,
      "MaxConcurrentPerUser": 10,
      "BurstAllowance": 2,
      "ExemptTypes": ["notification"]
    }
  }
}

// Registration
builder.Services.AddInteractionHook<RateLimitInteractionHook>();

What Happens When the Limit Is Hit

When a publish attempt exceeds a rate limit, the hook throws InteractionRateLimitException:

// Thrown by RateLimitInteractionHook
public class InteractionRateLimitException : InteractionBlockedException
{
    public string UserId { get; }
    public string LimitType { get; } // "per_minute" | "concurrent"
    public int CurrentCount { get; }
    public int Limit { get; }
    public TimeSpan RetryAfter { get; }
}

The pipeline catches this and returns a 429 Too Many Requests-equivalent error to the caller of PublishAndWaitAsync().

Exempt Interaction Types

Some interaction types (e.g., notification) are typically lower-priority and higher-volume. You can exempt specific types from rate limiting:

options.AuditLog.ExemptTypes = ["notification", "my-company/info-banner"];

High-Priority Override

Interactions with priority: "high" are granted additional burst allowance equal to the BurstAllowance setting. This allows critical approvals to break through a high-volume inbox without completely bypassing rate limits.

Custom Rate Limit Logic If you need more complex rate limiting (e.g., per-requester limits, per-type limits, tenant-level limits), implement a custom IInteractionHook with your own logic and register it before RateLimitInteractionHook. See the Custom Hooks page for the implementation pattern.

Distributed Rate Limiting

In multi-instance deployments, the rate limit counters are backed by a distributed cache (Redis by default when available, in-memory otherwise). This ensures the limit is applied globally across all server instances, not per-instance:

// Configuration for Redis-backed distributed rate limiting
builder.Services.AddStackExchangeRedisCache(options => {
    options.Configuration = "redis-host:6379";
});

builder.Services.AddInteractionHook<RateLimitInteractionHook>(); // auto-uses Redis if registered