Actions are Auth0's extensibility model — Node functions that run at points in the login flow. They replace Rules, which Auth0 has made read-only and ends on 2026-11-18.
Assumes Management API credentials are configured — see Configuration.
Auth0 reference: Actions.
Create, deploy, bind
Three steps, and the third is the one people miss.
alias Auth0Client.Management.Action
# 1. create — supported_triggers takes objects, not names.
# Action.triggers() lists the available ids and versions.
{:ok, action} = Action.create(%{
name: "enrich-profile",
supported_triggers: [%{id: "post-login", version: "v3"}],
code: "exports.onExecutePostLogin = async (event, api) => {};"
})
# 2. deploy — Auth0 accepts this asynchronously and answers 202
Action.deploy(action["id"])
# 3. bind — until this runs, the action never executes
Action.update_bindings("post-login", [
%{ref: %{type: "action_id", value: action["id"]}, display_name: "Enrich profile"}
])An action that is created and deployed but never bound is inert. Nothing errors; it simply never runs.
Binding replaces, it does not append
update_bindings/2 replaces every binding on the trigger
It is not additive. Passing one binding unbinds every other action on that trigger. The order you give is the order Auth0 executes them in.
To add an action without disturbing the others, read the current list first:
{:ok, %{"bindings" => existing}} = Action.bindings("post-login")
Action.update_bindings("post-login", existing ++ [new_binding])Iterating
update/2 changes the draft; deploy/1 makes it live. To try a change without deploying it:
Action.test(action["id"], %{user: %{email: "test@example.com"}})Versions are kept, so a bad deploy can be rolled back by redeploying an earlier one:
Action.versions(action["id"])
Action.deploy_version(action["id"], "ver_abc123")Pass %{update_draft: true} to overwrite the draft with that version's code as well, rather than only
redeploying it.
Deleting
Auth0 refuses to delete an action that is still bound to a trigger. Either unbind it first, or force it:
Action.delete(action["id"], force: true)Debugging a run
Action.execution/1 returns the details of one execution, including logs from each action bound to the
trigger — the place to look when an action ran but did not do what you expected.