Sign in →
1 min read

MuleSoft Integration (Custom Policy)

Publish the Aforo metering policy to your Anypoint Exchange and apply it in API Manager, from Aforo in one click or yourself with Maven.

Updated 2026-11-11Suggest edits

Task Overview#

JIRA-READY TASK
Publish the Aforo metering policy to Exchange and apply it to your APIs.
Estimated time: 5 minutes from Aforo, 25 minutes by hand. Target: MuleSoft Platform Architect.
WARNING
The policy package builds and its structure is tested, but it has not yet been certified against a live Anypoint organization. Apply it to a test API and run the verification steps before you bill from it.

What the policy does#

aforo-metering is a Mule 4 custom policy. You apply it in API Manager like rate limiting or JWT validation; your Mule applications are not rebuilt or redeployed. After each response it builds one usage event and posts it to Aforo from an async scope, so the API call is not delayed and is never failed by the policy.

The event is built once and sent up to three times, one second apart, on a transport error, a 5xx or a 429. Every attempt carries the same idempotencyKey, so a redelivery is deduplicated instead of billed twice.

Prerequisites#

REQUIRED
Mule runtime 4.4.0 or later
CloudHub, CloudHub 2.0, Runtime Fabric or self-managed, with the API managed in API Manager. Flex Gateway and Mule 3 are not supported.
REQUIRED
JWT Validation policy on the API
MuleSoft’s own policy. The metering policy reads the customer id from the claims it verifies.
REQUIRED
Exchange Contributor + Manage Policies
On the connected app or user that publishes the asset and applies the policy.
REQUIRED
Aforo API key and workspace id
Key from Workspace Admin → API Keys.
REQUIRED
Outbound HTTPS to api.aforo.ai:443
From the runtime that hosts the API.
MANUAL ONLY
JDK 17+ and Maven 3.9
Only for the manual install.

Install from Aforo (one click)#

Use this when the MuleSoft connection in Aforo already points at the organization and environment that host your APIs.

1In Aforo, open Integrations → your MuleSoft connection → Metering.
2Set the exclusions and status overrides if you need them, then choose Deploy.
3Apply MuleSoft’s JWT Validation policy to each API if it is not there already.

Deploy does three things in your Anypoint organization:

StepWhat Aforo does
PublishChecks Exchange for aforo-metering at the current version. If it is missing, uploads the policy jar and its definition as a new asset in your organization.
ApplyApplies the policy to every Mule 4 API instance in the connected environment, with your Aforo endpoint, API key and workspace id.
UpdateWhere the policy is already applied, updates its configuration in place. An instance never ends up with two copies.
INFO
Deploy reports a failure, and applies nothing, when the asset cannot be published. If some instances refuse the policy, the message lists them; choose Deploy again and only those are retried. Remove takes the policy off every instance and leaves the asset in Exchange, where it is inert.

Install yourself: publish to Exchange#

A custom policy is two files in Exchange: a jar and a definition YAML. API Manager cannot use a YAML uploaded on its own, so the policy ships as a Maven project that builds and publishes both.

terminal
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-gateway-plugins/mulesoft/aforo-metering

# Build only (writes target/aforo-metering-1.0.0-mule-policy.jar)
mvn clean package

Add your Exchange credentials to ~/.m2/settings.xml. For a connected app, the username is the literal ~~~Client~~~ and the password is the client id and secret joined by ~?~:

~/.m2/settings.xml
<servers>
  <server>
    <id>exchange-server</id>
    <username>~~~Client~~~</username>
    <password>CLIENT_ID~?~CLIENT_SECRET</password>
  </server>
</servers>

Publish. Exchange only accepts an asset whose group id is your organization id (Access Management → Organization), so pass it:

terminal
mvn clean deploy -Danypoint.org.id=YOUR_ORG_ID
INFO
An Exchange version cannot be overwritten. To publish a change of your own, raise <version> in pom.xml first.

Install yourself: apply in API Manager#

1Open API Manager and select the API instance.
2Policies → Add policy. Apply JWT Validation first if the API does not have it.
3Add policy again → Custom → Aforo Usage Metering.
4Fill in the three required properties (table below) and apply.
5Check the policy order: Aforo Usage Metering must come after JWT Validation.

To script it, send the same thing to the API Manager API:

terminal
curl -X POST \
  "https://anypoint.mulesoft.com/apimanager/api/v1/organizations/$ORG_ID/environments/$ENV_ID/apis/$API_INSTANCE_ID/policies" \
  -H "Authorization: Bearer $ANYPOINT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "groupId": "'"$ORG_ID"'",
    "assetId": "aforo-metering",
    "assetVersion": "1.0.0",
    "pointcutData": null,
    "configurationData": {
      "aforo-endpoint": "https://api.aforo.ai/v1/ingest/batch",
      "aforo-api-key": "sk_live_your_key_here",
      "aforo-tenant-id": "your-workspace-id"
    }
  }'

Configuration#

PropertyWhat it doesRequired
aforo-endpointIngest batch URL: https://api.aforo.ai/v1/ingest/batchYes
aforo-api-keySent as the X-API-Key header. Masked in API Manager; encrypted on runtimes with gateway encryption onYes
aforo-tenant-idYour workspace id. Sent as X-Tenant-Id and stamped on every eventYes
customer-id-claimJWT claim that holds the Aforo customer idNo. Default customer_id, then sub
tenant-id-claimJWT claim that holds the workspace id. Set it only when one API serves several workspacesNo. Default: use aforo-tenant-id
mcp-enabledClassify JSON-RPC tools/call POSTs as MCP tool invocations. Reads the JSON request bodyNo. Default false
mcp-product-idAforo product id of the MCP serverNo
status-outcomesexecutionStatus overrides per status code, e.g. 404=VALIDATION_FAILEDNo
exclude-status-codesStatus codes that produce no event. A value replaces the defaultNo. Default 401,403,429; none meters all
exclude-pathsPath prefixes that produce no event. A value replaces the defaultNo. Default /health,/ready,/metrics; none meters all
INFO
A value must not contain a double quote: the policy places each value inside a quoted string, and Aforo refuses such a value at deploy time.

Where the customer id comes from#

The policy reads the customer id from the claims that MuleSoft’s JWT Validation policy has verified: the customer-id-claim claim, then sub. It reads no request header for identity, so a caller cannot bill another customer by sending X-Client-Id or X-Customer-Id.

WARNING
A request with no verified customer id produces no event. The API call still goes through. If your API uses client-id enforcement only, with no JWT, nothing is metered.

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 optional status-outcomes policy parameter. Format: comma-separated code=STATUS pairs:

aforo-policy-config.yaml
status-outcomes: "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
Policy parameter exclude-status-codes401,403,429The word none
Policy parameter exclude-paths/health,/ready,/metricsThe word none

Leave a parameter empty to get its default. 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.

aforo-policy-config.yaml
# also skip 404s and everything under /internal
exclude-status-codes: "401,403,429,404"
exclude-paths: "/health,/ready,/metrics,/internal"

What is not covered#

CaseWhat happens
Unhandled flow errorsIf the flow ends in an error that propagates out, the call is not metered. A flow whose error handler sets the status and returns normally is metered.
Flex Gateway, Mule 3The policy is not offered. One-click deploy skips those API instances.
Client-id-only APIsNo verified customer id, so no event.
JSON-RPC errors in a 2xxThe response body is never read; an MCP call that returns an error object with HTTP 200 is SUCCESS.
Runtime clock zoneoccurredAt is formatted as UTC from the runtime’s local time. CloudHub runs in UTC; start a self-managed runtime with -Duser.timezone=UTC.
Quota and margin enforcementThis policy meters. It does not block or throttle a request.

Verification#

1In API Manager, the API instance lists Aforo Usage Metering as applied, after JWT Validation.
2Send one request with a valid JWT. In Aforo, open Intelligence → Usage Ingestion: one event arrives with the token’s customer id and your workspace id.
3Send the same request with X-Client-Id: someone_else. The event still carries the token’s customer.
4Send a request that returns 401 and one to /health. No event arrives.
5Check the runtime log (Runtime Manager → Logs) for [aforo-metering]. No line means every send succeeded.
terminal
# Applied policies on the instance: look for assetId "aforo-metering"
curl -s \
  "https://anypoint.mulesoft.com/apimanager/api/v1/organizations/$ORG_ID/environments/$ENV_ID/apis/$API_INSTANCE_ID/policies" \
  -H "Authorization: Bearer $ANYPOINT_TOKEN"

# One metered call
curl -s "https://your-api.example.com/v1/orders" -H "Authorization: Bearer $CUSTOMER_JWT"

Troubleshooting#

SymptomCauseFix
Deploy fails: “Could not publish the Aforo policy to your Exchange (HTTP 403)”The connection lacks Exchange ContributorAdd the permission to the connected app, then Deploy again
Deploy fails: “no Mule 4 API instance”The connected environment has only Flex Gateway or Mule 3 instances, or noneConnect the environment that hosts the Mule 4 APIs
mvn deploy returns 401 or 403Wrong exchange-server credentials, or no Exchange Contributor roleCheck the ~~~Client~~~ username and the id~?~secret password
mvn deploy returns 409That version is already in your ExchangeRaise <version> in pom.xml
The policy is not listed in API ManagerThe instance is Flex Gateway or Mule 3, or the asset is in another organizationUse a Mule 4 instance in the organization you published to
No events, API calls succeedNo verified JWT claims: JWT Validation is missing or runs after this policy, or the token has neither customer_id nor subOrder JWT Validation first; set customer-id-claim
401, 403 and 429 calls are missing from usageThey are excluded by defaultSet exclude-status-codes to none
Log shows “usage event not delivered”The runtime cannot reach api.aforo.ai:443, or the API key is wrongAllow outbound HTTPS; check the key in the policy configuration
Events rejected as “in the future”The runtime’s time zone is not UTCStart the runtime with -Duser.timezone=UTC
Aforo Documentation — Aforo Docs