Verifying live

The checks only the real gateway can do — calling your capability, always sending a body on a POST with a request schema, calling /defaults explicitly, and using request IDs.

A green test suite proves your handler works. It does not prove the gateway routes to it, that the install wrote your routes, or that your declared contract accepts the requests you meant it to. Those need real calls.

Do this on staging first, then repeat it on production after that deploy.

The four checks

1. The app answers
curl -i https://api.revenexx.com/v1/serials/ \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT"

The health route answers without a tenant identity, so a 200 here means the app is deployed and reachable at all. A 404 means the install step did not run for this version.

2. Seed the defaults
curl -s -X POST https://api.revenexx.com/v1/serials/defaults \
  -H "X-Revenexx-Api-Key: $REVENEXX_API_KEY" \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT" \
  -H "Content-Type: application/json" \
  -d '{}'
3. Read something back
curl -s "https://api.revenexx.com/v1/serials?limit=50&order=created_at.desc" \
  -H "X-Revenexx-Api-Key: $REVENEXX_API_KEY" \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT"
4. Confirm the contract
curl -s https://api.revenexx.com/v1/openapi.json \
  -H "X-Revenexx-Api-Key: $REVENEXX_API_KEY" \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT" \
  | grep -o '"/serials[^"]*"'

Step 4 is the one people skip and the one that catches the most. See The published OpenAPI document.

Always send {} on a POST with a request schema

A POST with no body at all fails the gateway's contract validation before your function is invoked, when the capability declares a request schema. Send {} if you have nothing to say:

Request
curl -X POST https://api.revenexx.com/v1/serials/defaults \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT" \
  -H "X-Revenexx-Api-Key: $REVENEXX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

The failure is confusing because your handler is never reached and nothing appears in your execution log: it looks like your app rejected the call, and your app never saw it. If you get a 400 on a POST you are sure about, check the body and the Content-Type before you check your code.

The same mechanism is behind the stricter-contract trap — validation happens in front of you, so a 400 you cannot explain locally usually means the contract, not the handler.

Call /defaults explicitly

Do not rely on app.installed to have seeded anything. It does not reliably fire on a Marketplace install.

Expose an idempotent /defaults capability, seed on the event as well, and call the route once after every install. Every shipped app does exactly this; it is not a workaround for a bug you can wait out, it is the reliable path.

Because it is idempotent, calling it twice is free — so put it in your deploy script rather than in a runbook nobody reads:

After deploy
revenexx deploy app --owner "$REVENEXX_TENANT"

curl -s -X POST "https://api.revenexx.com/v1/serials/defaults" \
  -H "X-Revenexx-Api-Key: $REVENEXX_API_KEY" \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT" \
  -H "Content-Type: application/json" -d '{}'

Quote the request ID

Every response carries an X-Request-ID; the gateway generates one if you did not send your own. Use -i to see it:

Request
curl -i "https://api.revenexx.com/v1/serials?limit=1" \
  -H "X-Revenexx-Api-Key: $REVENEXX_API_KEY" \
  -H "X-Revenexx-Tenant: $REVENEXX_TENANT"

It is the correlation id for the request end to end, so:

  • Log it on the client side for anything you might have to ask about later.
  • Quote it in a support request. It is what makes "it failed at 14:20" answerable.
  • Forward it when your app calls another app, so one trace spans the whole chain.

Two other response headers worth reading while you are here: X-Capability-App names the app that served the capability — which is how you confirm an override is in the path — and X-Cache tells you whether a cacheable response was a HIT. See API usage.

Then check the parts curl cannot

  • Open the app in the Cockpit. Views render, existing data survived, no empty screens from a mistyped column name. See How the Cockpit renders.
  • Read the executions. Every call you just made should be there, with the status you expected. A call you made that is not there was rejected before it reached you. See Operating.
  • Check the settings form. The keys you declared, grouped, with sensible defaults.
  • Wait for a schedule, or wait for the next tick. A cron job starts firing once the version declaring it is installed.

Reading a failure

You getUsually
404 on your pathThe install did not run for this version, or the app is not installed on this tenant
400 on a POST you are sure aboutNo body, or a contract stricter than your handler
403 on a writeThe entity grant does not cover that operation
401The credential, not your app
Same fifty rows on every pageThe list capability does not declare limit/offset
502Your function threw, or something it called did. Read the execution

Next steps

  • Deploying — the release checklist this fits into.
  • Operating — the screens that explain a failure.
  • Testing — what to check before you get here.
  • API usage — headers, errors, rate limits.
Was this page helpful?