Sign in →
1 min read

Apigee Integration (Shared Flow)

Deploy the Aforo Shared Flow to your Apigee Organization. Decouple monetization logic from individual API proxies.

Updated 2026-11-11Suggest edits

Task Overview#

JIRA-READY TASK
Deploy the Aforo Shared Flow to your Apigee Organization.
Estimated time: 20 minutes. Target: Platform Architect.
apigee-shared-flow.architectureLIVE
CLIENT
API Request
inbound
Developer App calls Apigee proxy with API key in header.
0ms
PRE-FLOW
Entitlement
PreProxyFlowHook
Shared Flow checks KVM cache for tenant + product entitlement.
<3ms
TARGET
Backend
target endpoint
Request proxied to upstream service. Response captured for metering.
passthrough
POST-FLOW
Metering
PostProxyFlowHook
Builds one usage event from the request and the response status.
<5ms
EGRESS
Aforo Ingest
api.aforo.ai
TLS POST with the API key in X-API-Key, sent before the client gets its response. One retry with the same idempotency key.
tens of ms; 4s cap
apigee shared flow → aforo ingest · attached via global flow hook// p95 < 5ms

Why Shared Flow?#

Aforo uses an Apigee Shared Flow to decouple monetization logic from individual API proxies. This means:

One deployment
A single Shared Flow handles metering and entitlements for every proxy in your org
Zero proxy edits
Attach via Flow Hook — no XML edits to individual proxy bundles
Atomic updates
Update Aforo logic once. Every proxy picks it up on the next request.
Full Margin Guard
L1-L3 enforcement runs before traffic reaches your backend target

Prerequisites#

REQUIRED
Apigee X or Apigee Edge
Organization with admin or environment-level deploy rights
REQUIRED
Management API access
Authenticated access to Apigee Management API or Apigee UI console
REQUIRED
Aforo API Key
sk_live_* key from Workspace Admin → API Keys
REQUIRED
KVM permissions
Ability to create or update Key Value Maps in the target environment

One-Click Install from Aforo#

If your Apigee organization is already connected under Governance → Integrations, you don't need Steps 1–3. Open the connection, turn on metering and deploy. Aforo runs the same Management API calls you would:

#What Aforo doesWhere to see it in Apigee
1Creates the encrypted KVM and writes your settingsKey value map aforo-metering-config (organization scope)
2Imports the shared flow bundleShared flow aforo-metering, a new revision only when the bundle changed
3Deploys that revisionThe environment set on the connection
4Attaches it with continueOnError: trueThat environment’s PostProxyFlowHook

Deploy again after changing a setting: the KVM is refreshed every time, and the flow picks the change up within five minutes (it caches KVM reads for 300 seconds). Removing metering detaches the hook, undeploys the flow and deletes the entries Aforo wrote. The shared flow is deleted once no environment has it deployed.

WARNING
An environment has one PostProxyFlowHook slot. If another shared flow already holds it, Aforo does not replace it: the deploy stops and names that flow. Call aforo-metering from it with a FlowCallout policy, set meteringAttachMode to none on the connection, and deploy again.
Deploy saysCauseFix
Apigee refused to … (HTTP 403)The connection’s credential can read the organization but not change it.Grant it the Apigee Environment Admin and Apigee API Admin roles, then deploy again.
PostProxyFlowHook already runs the shared flow …The hook slot is taken.FlowCallout from that flow plus meteringAttachMode = none (see above).
Apigee reports the deployment … as ERRORThe revision did not start in that environment.Open the shared flow in the Apigee console; the deployment status shows the reason.

One-click install uses the Apigee X and hybrid Management API. It does not cover Apigee Edge; follow the manual steps below there. The rest of this page is the same install by hand.

Step 1: Import the Shared Flow Bundle#

The shared flow lives in the Aforo SDK distribution repo at aforo-gateway-plugins/apigee/. There is no public registry release, so deploy from source — the apigee/sharedflowbundle/ folder is the source of truth, and apigeecli zips and imports it for you:

terminal
# Clone the SDK distribution repo and enter the Apigee plugin folder
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-gateway-plugins/apigee

# Create the shared flow from the bundle folder
apigeecli sharedflows create bundle \
  --name aforo-metering \
  --folder sharedflowbundle \
  --org "$APIGEE_ORG" \
  --token "$(gcloud auth print-access-token)"

# Deploy the imported revision to your environment
apigeecli sharedflows deploy \
  --name aforo-metering \
  --rev 1 \
  --env "$APIGEE_ENV" \
  --org "$APIGEE_ORG" \
  --token "$(gcloud auth print-access-token)"
INFO
Set $APIGEE_ORG to your Apigee organization name and $APIGEE_ENV to the target environment (e.g., prod, test). The bundle expects an organization-scoped KVM named aforo-metering-config — create it before the first call (next step).

Step 2: Configure the Key Value Map (KVM)#

The Shared Flow reads its configuration from an encrypted KVM. Create the KVM and set the Aforo credentials:

terminal
# Create the KVM (encrypted, organization-scoped — the bundle reads it at org level)
curl -X POST "https://apigee.googleapis.com/v1/organizations/{ORG}/keyvaluemaps" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "aforo-metering-config",
    "encrypted": true
  }'

# Set the API key
curl -X POST "https://apigee.googleapis.com/v1/organizations/{ORG}/keyvaluemaps/aforo-metering-config/entries" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "api_key", "value": "sk_live_your_key_here" }'

# Set the ingest endpoint
curl -X POST "https://apigee.googleapis.com/v1/organizations/{ORG}/keyvaluemaps/aforo-metering-config/entries" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "aforo_endpoint", "value": "https://api.aforo.ai/v1/ingest/batch" }'

# Set the workspace (tenant) id
curl -X POST "https://apigee.googleapis.com/v1/organizations/{ORG}/keyvaluemaps/aforo-metering-config/entries" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "tenant_id", "value": "your-workspace-id" }'
WARNING
Always use encrypted KVMs for API keys. Apigee encrypts the values at rest and in transit. Never store credentials in proxy-level variables or environment properties.

Step 3: Attach via Flow Hook#

Attach the Shared Flow to the environment's Post-Proxy Flow Hook. Every API proxy in the environment then sends a usage event after its response is built — no individual proxy edits. The flow reads the response status, so it belongs on the response path; it can't run in PostClientFlow, which accepts only FlowCallout, MessageLogging and ServiceCallout policies.

terminal
# Attach to the Post-Proxy Flow Hook (metering, after the backend responds)
curl -X PUT "https://apigee.googleapis.com/v1/organizations/{ORG}/environments/{ENV}/flowhooks/PostProxyFlowHook" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "sharedFlow": "aforo-metering",
    "continueOnError": true
  }'
PRO TIP
Keep continueOnError: true so a metering failure never changes the API response. The event is sent before the client receives the response: a healthy ingest endpoint adds tens of milliseconds. When Aforo ingest is unreachable the API call still succeeds, and the wait is bounded: each attempt gives up after 1 second to connect and 2 seconds for the response, and there is one retry, only after a connection failure or a 5xx that failed within 1.2 seconds. That is about 2 seconds added per call while ingest is unreachable, about 3 seconds if it accepts the connection and never answers, and about 4 seconds at the limit. The usage event for that call is not delivered: the flow sets aforo.meteringDeliveryFailed and prints USAGE EVENT DROPPED to the debug session. Set the KVM entry max_retries to 0 to send once (about 1 second). The two timeouts are fixed in the shared flow's send policies; Apigee does not read ServiceCallout timeouts from a variable.

How Calls Get an Outcome#

Read this if any rate plan behind this gateway uses Outcome-Based pricing — it bills each call at a weight for its executionStatus. Every other pricing model ignores the field. The plugin sets it from the upstream response status:

Upstream statusexecutionStatus
2xx, 3xxSUCCESS
408, 504TIMEOUT
499 (client closed the request, where the gateway reports it)CANCELLED
400, 422VALIDATION_FAILED
401, 403, 429BLOCKED
Every other 4xx and 5xx (404 included)ERROR
Missing, 0, 1xxNot sent — the call bills at full weight
WARNING
A status with no weight on the rate plan bills at full weight (1.0). The rate plan wizard starts BLOCKED and CANCELLED at 0, but plans saved before those two statuses were added to the wizard (September 30, 2026) have no weight for them, so the gateway's BLOCKED and CANCELLED calls on those plans bill in full until you give them one — open the plan in the wizard and enter a weight (the fields show blank), or set it through the rate-plan API (outcomeWeights). See Outcome-Based pricing.

Override the status for specific codes

When the default table is wrong for your API — say a 404 is a normal answer, not a failure — map exact codes with the status_outcomes entry in the aforo-metering-config KVM. Format: comma-separated code=STATUS pairs:

terminal
curl -X POST "https://apigee.googleapis.com/v1/organizations/{ORG}/keyvaluemaps/aforo-metering-config/entries" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "status_outcomes", "value": "404=SUCCESS,429=ERROR" }'

Keys are exact HTTP codes from 200 to 599. Values must be one of the 11 statuses: SUCCESS, PARTIAL, TIMEOUT, ERROR, VALIDATION_FAILED, FAILED, FAILURE, CANCELLED, PENDING, BLOCKED, HITL_REQUIRED. Invalid entries are ignored without a warning. If a code is listed twice, the last valid entry wins. An override can't give a status to a missing, 0 or 1xx code.

Requests that aren't metered

By default, responses with status 401, 403 or 429 and requests to /health, /ready or /metrics produce no event at all — they cost nothing whatever the rate plan says. All five gateway plugins share this default. A path is skipped when it equals an entry or starts with it, so /health also covers /health/live.

SettingDefaultTo meter everything
KVM entry exclude_status_codes in aforo-metering-config401,403,429The word none
KVM entry exclude_paths/health,/ready,/metricsThe word none

Leave an entry out to get its default. A KVM entry can't be stored empty, which is why metering everything takes the word none. KVM reads are cached for five minutes. Entries that aren't whole numbers from 100 to 599 are ignored. A list you set replaces the default rather than adding to it: a status list of 404,500 skips only those two and starts metering 401, 403 and 429. A status you choose to meter bills as BLOCKED unless you override it above.

Margin Guard Enforcement#

The PreProxy Flow Hook executes all three Margin Guard levels before traffic reaches your backend target:

LevelTriggerApigee ActionHTTP Response
L1Margin < 20%JavaScript callout → webhook200 + X-Aforo-Warning header
L2Margin < 10%Assign Message → inject throttle header200 + X-Aforo-Throttle: true
L3Margin < 0%Raise Fault → block request429 Margin Limit Exceeded
WARNING
L3 enforcement uses Apigee's RaiseFault policy. The request is rejected at the gateway — your backend never sees it. This protects compute costs from unprofitable traffic.

Verification#

Verify the deployment by checking the Flow Hook attachment and sending a test request:

terminal
# Verify Flow Hook is attached
curl -s "https://apigee.googleapis.com/v1/organizations/{ORG}/environments/{ENV}/flowhooks/PostProxyFlowHook" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" | jq .

# Expected: { "flowHookPoint": "PostProxyFlowHook", "sharedFlow": "aforo-metering", "continueOnError": true }

# Verify the revision is deployed (state should be READY)
curl -s "https://apigee.googleapis.com/v1/organizations/{ORG}/environments/{ENV}/sharedflows/aforo-metering/deployments" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" | jq .

# PaperPlaneTilt a test request through any proxy in the environment
curl -v https://{ORG}-{ENV}.apigee.net/your-api/endpoint \
  -H "X-Tenant-Id: test_tenant_123" \
  -H "Authorization: Bearer sk_live_customer_key"

# Check for Aforo headers in response:
# X-Aforo-Remaining: 8420
# X-Aforo-Plan: enterprise

# Then confirm it arrived: in Aforo, open Intelligence → Usage Ingestion.
# The event shows up once the plugin has sent it (batching plugins flush every few seconds).
PRO TIP
If events are not appearing, check the Apigee Debug (Trace) tool. Look for the aforo-metering Shared Flow execution after the proxy response (look for the aforo.eventPayload variable). Common issues: KVM not found (wrong environment), expired Aforo API key, or network policy blocking outbound HTTPS to api.aforo.ai.
Aforo Documentation — Aforo Docs