Skip to main content

Quick diagnostics

When something fails, work through this checklist first before diving into specific errors:
PymtHouse has two client IDs per app. Using the wrong one causes the most common integration errors.Golden rule: The path {clientId} and the JWT client_id / azp claim always use the public app_… id. HTTP Basic credentials always use the M2M m2m_… id.
The four required variables — and the clients they correspond to:
If PYMTHOUSE_PUBLIC_CLIENT_ID starts with m2m_ (or vice versa), every flow will fail.
You must call POST .../users before calling POST .../users/{id}/token. If the user does not exist in PymtHouse, the token mint returns 404.

Error reference

400 invalid_scope

Where it appears: Token endpoint (POST /api/v1/oidc/token) or user-token mint (POST .../users/{id}/token) Cause A — Public client missing the requested scope The scope you’re requesting (e.g. sign:job) must be in the public client’s allowed_scopes. The user-token mint validates the requested scope against the public client, not the M2M client. Fix: Add the missing scope to the public app_… client’s allowed_scopes in the dashboard. Cause B — M2M client missing users:token Minting user JWTs requires the M2M client to have users:token in its allowed_scopes. Fix: Add users:token to the M2M m2m_… client. Cause C — Requesting admin scope admin scope is explicitly rejected on the user-token mint endpoint. Fix: Remove admin from the requested scope. Diagnostic:

400 programmatic / per_user

Where it appears: User-token mint or programmatic token flow Cause: The public client’s allowed_scopes must include users:token to enable programmatic (non-interactive) user token issuance. Fix: Add users:token to the public app_… client’s allowed_scopes. Note: this also switches the app’s billing mode to per-user, so per-user usage attribution becomes available in the Usage API.

400 invalid_grant on device poll

Where it appears: Token polling after device flow (POST /api/v1/oidc/token with grant_type=device_code) Message: grant request is invalid after the browser completed login Cause: The device grant’s accountId could not be resolved. This happens when the subject_token used in the RFC 8693 device binding step has a sub that maps to an app_users row, but that row hasn’t been linked to an end_users / users record via findOrCreateAppEndUser. Fix: Ensure the subject_token in the device-completion call is minted via POST .../users/{externalUserId}/token (Builder API), not a raw client_credentials token. The Builder API mint creates the end_users linkage.

404 Not Found on valid credentials

Where it appears: Any Builder API or Usage API endpoint Cause A — M2M client from a different app The M2M client’s app does not match the {clientId} in the URL path. PymtHouse returns 404 (not 403) for security — it does not reveal whether the path clientId exists. Fix: Confirm M2M_ID and PUBLIC_CLIENT_ID belong to the same registered developer app. Cause B — Wrong client ID type in the URL Using m2m_… as the URL {clientId} instead of app_…. Fix: All /api/v1/apps/{clientId}/… paths use the public app_… client id in the URL. Cause C — User not yet provisioned Calling /users/{externalUserId}/token before /users to provision the user. Fix: Call POST /users with the externalUserId before minting tokens for that user.

402 owner_payment_method_required

Where it appears: User provisioning (POST /users) or allowance grant Cause: The app owner’s wallet is empty and no payment method is attached. When ACTIVATION_GATE_MODE=enforce, new user provisioning is blocked until the owner adds a payment method. Fix: Go to your billing settings and attach a payment method, or call:

403 end_user_cap_reached

Where it appears: User provisioning (POST /users) or allowance grant Cause: The app has reached its configured endUserCap limit on total provisioned users. Fix: Increase the endUserCap on the app’s billing settings, or contact the platform admin.

403 stripe_connect_required

Where it appears: Paid plan checkout, plan change to a priced target Cause: The app is in enforce_revenue or enforce activation gate mode, and Stripe Connect is not fully set up (either charges_enabled or details_submitted is false). Fix: Complete the Stripe Connect onboarding. Check the current Connect status:

503 Service Unavailable on usage/balance

Where it appears: GET .../usage, GET .../usage/balance, GET .../users/{id}/allowances Cause: OPENMETER_URL is not configured, or the OpenMeter service is unreachable. All usage and allowance reads require OpenMeter. Fix: Verify OPENMETER_URL and OPENMETER_API_KEY are set in your deployment environment. Check connectivity from the PymtHouse service to the OpenMeter host.

Usage shows zero after AI requests

Cause A — Async metering delay Usage is metered asynchronously via Kafka → OpenMeter collector. There is typically a 10–30 second delay between a signed ticket and it appearing in the Usage API. Fix: Wait 30 seconds and query again. Cause B — OPENMETER_URL not configured If openMeterConfigured: false appears in the usage response, metering is not active. Fix: Configure OPENMETER_URL and bootstrap OpenMeter meters with npm run openmeter:bootstrap. Cause C — pipeline + modelId missing from the request groupBy=pipeline_model aggregates from validated billing events. Events that arrive without pipeline + modelId metadata appear only in totals, not in byPipelineModel. Fix: Ensure the gateway includes paymentMetadataVersion with pipeline and modelId in the payment payload. See the gateway payment metadata contract in the Builder API reference.

trial_credits_exhausted from the signer

Cause: The user’s hasAccess is false — their allowance balance is zero. Fix: Check the balance, then either grant a top-up or direct the user to a plan upgrade:

JWT session decrypt errors (NextAuth)

Symptom: Repeated JWT_SESSION_ERROR or JWEDecryptionFailed in server logs Cause: NEXTAUTH_SECRET changed or .env.local is overriding .env. Fix:
  1. Ensure NEXTAUTH_SECRET is stable across deployments.
  2. Check that .env.local does not contain a conflicting NEXTAUTH_SECRET.
  3. Clear browser cookies for the app origin and sign in again.

Auth model reference

HTTP Basic is required (not optional) for the canonical Usage API paths (/api/v1/builder/apps/…/usage*). Bearer machine tokens are not accepted there.

Getting help

If you’ve worked through this guide and are still stuck:
  1. Check the server logs — PymtHouse logs the specific validation that failed.
  2. Verify your credentials against the OIDC discovery document: GET {issuer}/.well-known/openid-configuration
  3. Use the interactive API docs at GET /api/v1/docs to test endpoints directly.