authyo

n8n community node for Authyo - send and verify one-time passcodes (OTP) over SMS, WhatsApp, Voice and Email

Package Information

Released: 2 mo ago
Downloads: 0 weekly / 0 monthly
Latest Version: 0.1.3

Documentation

@authyo/n8n-nodes-authyo

An n8n community node for Authyo — send and verify one-time passcodes (OTP) over SMS, WhatsApp, Voice, and Email, and manage the sessions they produce.

Installation · Credentials · Operations · Usage · Resources

Installation

n8n UI (self-hosted): Settings → Community Nodes → Install → @authyo/n8n-nodes-authyo

Manual:

cd ~/.n8n/custom
npm install @authyo/n8n-nodes-authyo

Restart n8n. "Authyo" then appears in the node panel.

Credentials

You need an Authyo application's App Key and App Secret, both found in the Authyo dashboard under your application's settings.

In n8n, create an Authyo API credential and paste them in. Press Test — this validates the pair against /auth/authentication without sending an OTP or spending credit.

Authorized Endpoint must be blank. If your Authyo application has an Authorized Endpoint (URL allow-list) configured, calls from n8n are rejected with Invalid endpoint. n8n is a server-to-server caller and sends no Origin/Referer header. Either leave Authorized Endpoint blank, or add your n8n instance's egress IPs to the allow-list.

Operations

OTP

Operation Description
Send Deliver a passcode to a phone number or email. Returns a mask ID identifying the OTP session.
Verify Check the code the user entered against its mask ID. Returns a session JWT on success.

Send supports an explicit channel (SMS, WhatsApp, Voice, Email) or Application Default, which uses the channel priority and fallback configured on the application. Optional: OTP length, expiry in seconds (60 second minimum), or supplying your own code.

Session

Operation Description
Verify Token Check whether a session JWT is still valid.
Revoke Invalidate a session token, logging the user out.

Application

Operation Description
Get Configuration Fetch the customization settings for the connected application.

Usage

Where the mask ID lives

Send OTP returns the mask ID nested inside an array:

{{ $json.data.results[0].maskId }}

Verify OTP needs that exact value. The session JWT that Verify returns is at data.token, not at the top level.

The maskId problem — and the clean way to solve it

Send and Verify are two separate events, separated by a human typing a code. The mask ID has to survive that gap.

The cleanest solution keeps everything inside one workflow execution, so the mask ID never has to be stored anywhere:

Form Trigger            (page 1 — collect email or phone)
  └─ Send OTP           (Authyo node, renamed "Send OTP")
       └─ Form          (page 2 — asks the user for the code)
            └─ Verify OTP   (Authyo node — maskId still in scope)
                 └─ IF  {{ $json.success }}
                      ├─ true  → Form Ending / your action
                      └─ false → back to the Form page to re-prompt

The Form node continues a multi-page form on the same execution, so a previous node's output stays reachable:

{{ $('Send OTP').first().json.data.results[0].maskId }}

The node name in that expression must match the node's actual name on the canvas. The diagram above assumes the first Authyo node was renamed to Send OTP. If you leave the default names, they will be Authyo and Authyo1, and the expression becomes $('Authyo').first().... Use .first() rather than .item — after a pause, .item has no paired input item to resolve against.

Alternative — the Wait node. If your workflow does not begin with a Form Trigger, use a Wait node with resume mode On Form Submitted instead of the Form node. It also pauses the same execution, so the same expression works. Two things to set:

  • Enable Limit Wait Time and set it slightly beyond your OTP expiry, so an abandoned run does not sit forever.
  • The Wait node has a single output — there is no separate timeout branch. Put an IF after it if you need to distinguish "resumed" from "timed out".

While building either version, use the Test URL and click Execute workflow. The Production URL only serves an active workflow and gives you no execution log to debug against.

Across separate workflows. If you send from one workflow and verify from another (e.g. a webhook your own front-end calls), no execution is shared, so you must persist the mask ID yourself — keyed by phone or email in Postgres/Redis — or return it to the front-end and have it send the value back on verify.

Error handling

Authyo answers HTTP 200 even on failure, signalling the real outcome via success: false in the body. n8n keys off the status code, so this node inspects the body and raises a proper n8n error — invalid credentials, a blocked account, or a rate limit will fail the node rather than silently continuing down the success branch.

One case is deliberately not an error: a wrong or expired code. That is the expected outcome of Verify, not a broken workflow, so it is passed through as normal data with success: false. Branch on it with an IF node:

{{ $json.success }}     → true: verified   false: wrong or expired code
{{ $json.message }}     → e.g. "Invalid OTP", "OTP has expired"

This means you do not need Continue-On-Fail to handle a mistyped code — and you should not enable it, because it would also swallow genuine credential and rate-limit errors, which are the ones you want to hear about.

Response fields worth knowing

What Where
Mask ID data.results[0].maskId
Session JWT data.token — not at the top level
Channel actually used data.results[0].authType
Cost of the send data.results[0].charge / .currency
Throttle backoff retryAfterSeconds, when present

Resources

License

MIT

Discussion