A new user signs into your developer portal for the first time. They open the API reference, hit the playground, and there’s nothing there. No key. One more click between them and their first successful request.
That click doesn’t need to be there. Most mainstream auth providers expose a hook that fires when a user signs in, and you can use it to create their API key before they ever land on the portal. By the time the page renders, the key already exists.
In a Zuplo developer portal, the payoff lands directly in the playground. The user signs in, the API playground is already populated with their key, ready to use, and the first call works without anyone hunting for a button.
This post walks through the pattern using Auth0 Actions, the easiest to wire up. The closing section covers how the same idea maps to Clerk, Supabase, and Firebase Auth.
- Running a Zuplo developer portal with custom authentication
- Trying to give every signed-in user a working API key on first load
- Looking to make the "Time to Hello, World" even shorter

Provision API Keys at First Login
Watch the video walkthrough of wiring an Auth0 Post Login Action to the Zuplo Developer API so a new user lands on the portal with a working key.
How the flow works

Auth0 fires a Post Login Action every time a user signs in. The Action runs server-side, so it can hold a Zuplo API key as a secret and call the Developer API on the user’s behalf.
On first login, the Action creates a Zuplo consumer, asks for an API key to be
generated alongside it, and stores the consumer’s ID in the user’s
app_metadata. On every subsequent login, it sees the metadata and exits.
That’s the whole pattern. One Action, one fetch call, one piece of metadata.
Pro tip:
In Zuplo, a consumer owns one or more API keys, usually one per end user
or integration. A bucket holds those consumers and is scoped per
environment (working copy, production, or shared). The Developer API is
Zuplo’s management plane at dev.zuplo.com, the API you call to create
consumers and keys programmatically.
Step 1: Get a Zuplo Developer API key
Open your Zuplo account settings and create an API key for the Developer API. Copy it. You’ll paste it into Auth0 in a moment.
While you’re there, note your account name and the bucket you want consumers
created in. Both live under Services > API Key Service in the portal.
Buckets are scoped per environment, so pick the one matching the environment your portal points at. If you run separate Auth0 tenants for preview and production, set up the Action in each and point it at the matching bucket.
Step 2: Create the Auth0 Action
In the Auth0 dashboard, go to Actions > Triggers and pick the post-login
trigger from the Sign Up & Login section (the one that fires after a user is
authenticated but before the token is issued). Click Create Action to
scaffold a new Action under that trigger and name it something like
create-zuplo-consumer.
Click the key icon in the editor sidebar and add a secret called API_KEY with
the Zuplo Developer API key you copied. Then drop in the following code,
swapping in your account and bucket names:
A few things worth pointing out.
The with-api-key=true query param tells Zuplo to mint an API key in the same
request. Without it, you’d need a second call. One round trip beats two,
especially inside a login flow.
The managers field is the ownership link, not a display field. When the
developer portal authenticates a user, it looks up any consumer whose managers
array contains that user’s email and renders their keys. That’s how the freshly
minted key shows up on the portal page without any extra glue.
Pass multiple emails if more than one identity should own the same consumer. If
you allow sign-in connections that don’t return an email (some SAML or social
providers), add an early return for !event.user.email before the fetch.
The try / catch deliberately swallows errors. If Zuplo is briefly unreachable,
you don’t want to lock the user out of the portal. The Action logs the failure
and exits clean. Next login, the metadata check is still empty, and the Action
retries.
One edge case to know about: if the Developer API succeeds but setAppMetadata
fails before Auth0 saves it, the next login will create a second consumer for
the same user. The managers link still works (the portal sees both), and you
can sweep duplicates on the Developer API. Rare enough to ignore until it isn’t.
Common mistake:
This pattern relies on Zuplo’s keys being retrievable: the key is created server-side without ever being shown to the user, and they retrieve it from the developer portal on first visit. Buckets configured for irretrievable keys (hashed at creation, shown once and never again) won’t work, since the user would never see the key. Zuplo defaults to retrievable, which is what makes this whole flow possible.
Step 3: Deploy and attach the Action to the trigger
Hit Deploy on the Action. Back on the post-login trigger page, drag your new Action into the flow between Start and Complete and save it.

The next time a user signs into your portal, they’ll have a consumer and a working API key before the page finishes loading.
Pro tip:
If you also use Zuplo’s built-in monetization, skip this entire setup. The monetization flow handles consumer creation for you, including assigning users to plans and billing them through Stripe.
Create an API Key Consumer on Login
The full reference for the Auth0 Actions integration, including the Developer API endpoints used.
Step 4: Connect Auth0 to your developer portal
The Action provisions the consumer, but the developer portal still needs to know
how to authenticate the same user so the managers email match resolves. Open
zudoku.config.tsx in your portal project and add an authentication block:
clientId and domain come from the Auth0 application’s Basic Information
page. audience is the identifier of the Auth0 API you create alongside the
application.
Three things have to line up before this works:
- The Auth0 application is a Single Page Web Application with
/oauth/callbackregistered as an allowed callback URL and/oauth/logout-callbackas an allowed logout URL. - An Auth0 API exists with the same identifier you pass as
audience. Without it, the portal can’t validate tokens Auth0 issues. - Refresh token rotation is enabled on the application with a 0-second overlap period, so the portal can keep the session alive across reloads.
Authenticate with Auth0
Full Zudoku auth config reference, including callback URLs, the Auth0 API requirement, and refresh token rotation.
Verify the Auth0 Action provisioned a key
Sign into your developer portal as a test user. Open the API key management
page. You should see a consumer named c-<uuid> with one key attached.

Check Auth0 > User Management > Users, click your test user, and look at the App
Metadata tab. api_consumer should be set to the consumer ID Zuplo returned.
That’s the receipt that the Action ran.
Run a request from the portal playground. It should authenticate without anything else from you.

How this maps to Clerk, Supabase, and Firebase Auth
The mechanism varies by provider, and that variance is where most teams get stuck:
- Clerk fires
user.createdwebhooks (eventually consistent, retried on failure). POST to a small endpoint that calls the Zuplo Developer API and stores the consumer ID in Clerk’spublicMetadataor your own database. - Supabase offers
Auth Hooks (HTTP endpoints
or Postgres functions) and Postgres triggers on
auth.users. The trigger pattern is the most direct: insert into a profile table, call out to Zuplo from an Edge Function on that table. - Firebase with Identity Platform has
Blocking Functions
on the
beforeCreatetrigger (the v2 SDK helper isbeforeUserCreated). Same shape as Auth0 Actions, deployed as a Cloud Function. Note the 7 second response budget. - PingFederate and generic OpenID providers don’t expose a hook of this kind. The fallback is provisioning on the first authenticated request to your portal’s backend.
Auth0 is the easiest because Actions are synchronous, run server-side with a secret store, and don’t need a second service to host the handler. The others get you to the same outcome with more wiring.
Extend the pattern with tags, metadata, and expiry
The same pattern extends in obvious directions. Add tags to the consumer payload to mark which Auth0 connection the user came from. Add metadata fields the API Key Authentication policy can read at request time for backend routing or per-tenant rate limits.
Use expiresOn on the key if you want trial users to lose access after a fixed
window. Mirror the setup with a Post User Deletion Action that calls DELETE on
the same consumer endpoint, so revoking a user in Auth0 revokes their API key in
the same step.
First login is the cheapest place to provision. The user is right there, the identity is fresh, and you have a Zuplo bucket waiting.
