Azure API Management

For Azure, the canonical managed-gateway pattern is APIM inbound policy calling Fairvisor decision endpoint before backend forwarding.

Architecture

Client -> Azure APIM inbound policy -> Fairvisor /v1/decision
                                  | allow -> backend service
                                  | reject -> 429 response

APIM policy sketch

Use inbound policy to call Fairvisor and branch on status:

<policies>
  <inbound>
    <base />

    <send-request mode="new" response-variable-name="fvDecision" timeout="0.3" ignore-error="false">
      <set-url>{{fairvisor-edge-url}}/v1/decision</set-url>
      <set-method>POST</set-method>
      <set-header name="X-Original-Method" exists-action="override">
        <value>@(context.Request.Method)</value>
      </set-header>
      <set-header name="X-Original-URI" exists-action="override">
        <value>@(context.Request.OriginalUrl.Path + context.Request.OriginalUrl.QueryString)</value>
      </set-header>
      <set-header name="Authorization" exists-action="override">
        <value>@(context.Request.Headers.GetValueOrDefault("Authorization", ""))</value>
      </set-header>
      <set-header name="X-Forwarded-For" exists-action="override">
        <value>@(context.Request.IpAddress)</value>
      </set-header>
    </send-request>

    <choose>
      <when condition="@( ((IResponse)context.Variables[\"fvDecision\"]).StatusCode == 429 )">
        <return-response>
          <set-status code="429" reason="Too Many Requests" />
          <set-header name="Retry-After" exists-action="override">
            <value>@(((IResponse)context.Variables["fvDecision"]).Headers.GetValueOrDefault("Retry-After", "1"))</value>
          </set-header>
          <set-header name="X-Fairvisor-Reason" exists-action="override">
            <value>@(((IResponse)context.Variables["fvDecision"]).Headers.GetValueOrDefault("X-Fairvisor-Reason", "rate_limited"))</value>
          </set-header>
        </return-response>
      </when>
    </choose>
  </inbound>
  <backend><base /></backend>
  <outbound><base /></outbound>
  <on-error><base /></on-error>
</policies>

Adjust policy syntax for your APIM tier/runtime version.

Failure policy

Decide behavior when Fairvisor is unavailable:

  • fail-closed: return 503/deny from policy
  • fail-open: skip deny and continue to backend

Use route-based split from Gateway Failure Policy.

Timeout guidance

  • keep decision timeout low (200-500 ms budget)
  • avoid retries in inbound policy hot path
  • alert on timeout spikes and downstream dependency errors

Security notes

  • Keep Fairvisor on private network path (VNet/private endpoint where possible)
  • Limit callers to APIM boundary only
  • Do not trust external X-Original-* headers from clients

Verification checklist

  1. Allowed request reaches backend
  2. Limited request returns 429 with Retry-After
  3. X-Fairvisor-Reason visible in logs and response
  4. Outage drill validates chosen fail-open/closed behavior

See Decision API for canonical header contract.