Every authenticator app starts from one line of text, and an OTPAuth URI builder assembles that line for you from three things: an issuer, an account name and a secret key. Paste in your values, copy the result, and any authenticator app can provision a 2FA entry from a scannable QR code in seconds. Below you will see how each part of the string is formed, which parts are required, which are optional, and how to avoid the escaping mistakes that quietly break enrollment.
How an OTPAuth URI Builder Assembles the String
The otpauth URI format follows a fixed shape that a consumer, meaning the authenticator app that imports it, can read without any further input from you. A producer, meaning your own service or the builder, writes it once and hands it over through a QR code or a link.
$$\texttt{otpauth://}\;\langle type\rangle\;/\;\langle label\rangle\;?\;\langle parameters\rangle$$The scheme name is always otpauth, the type decides how codes are produced, the label names the entry the user sees, and the query parameters carry the rest. A good builder fills each slot from a form field so you never type the punctuation by hand.
The Four Building Blocks
- Scheme: the literal text that tells the camera or operating system which app should open the link, so any otpauth:// URI is routed to an authenticator.
- Type: the choice between time-based and event-driven codes.
- Label: the issuer and account joined by a separator.
- Parameters: the secret and the extra settings that follow the question mark.
Choosing Between TOTP and HOTP Types
The first decision in any key URI is the type. TOTP is time-based: the code changes on a fixed schedule, usually every 30 seconds, and the formula behind it is defined in RFC 6238 and implemented by Google Authenticator and its peers. HOTP is counter-based: the code changes only when a new one is requested, so the string needs a starting value. Run the two factor qr code generator whenever you need another one — it only takes a few seconds.
$$\mathrm{TOTP} = \mathrm{HOTP}\left(K,\ \left\lfloor \frac{T - T_0}{X} \right\rfloor\right)$$Here \(K\) is the secret, \(T\) is the current time, \(T_0\) is the start time and \(X\) is the step in seconds. Unless you have hardware or a legacy system with special needs, choose TOTP. RFC 4226 describes the HOTP mechanism if you need the details.
Clock drift is the usual cause of failing codes even when the builder's string is correct: the time step comes from the device clock, so ask users to switch on automatic time.
TOTP versus HOTP at a glance
| Setting | TOTP | HOTP |
|---|---|---|
| Code changes | On a schedule | When requested |
| Extra parameter | Interval | Counter |
| Typical use | Almost every login | Hardware tokens |
Label and Issuer Rules
The label is what people read in their app. It takes the form Issuer:Account, where the issuer names the service and the account name is usually an email address. If you leave the issuer out, the app shows the account alone. Neither piece may contain a literal colon, because the colon is how the two are split.
Issuer parameter versus issuer label prefix
The service name appears twice on purpose. The issuer label prefix is display text before the separator, while the issuer parameter in the query string is the stable identifier many apps use for account matching. Google's convention says the two should be equal. Apple's password-manager guidance prefers a proper name in the label and a domain in the parameter. Keep them consistent unless you have tested your target apps, or users may see duplicate-looking entries.
Secret Key Requirements and Base32 Format
The secret key is the one mandatory query parameter. It must be written in Base32, which uses only the letters A to Z and the numbers 2 to 7, as defined in RFC 4648. Trailing padding with equals signs is not needed and should be omitted. Because that alphabet is already URI-safe, the secret never needs escaping.
Treat the value as a credential: it appears in plain text inside the generated link, so anyone who holds it can generate every future code. Generate it with a cryptographically secure random source on your server, store it securely, and deliver the finished string only over a secure channel.
Parameter values at a glance
| Parameter | Valid values | Default | Status |
|---|---|---|---|
| secret | Base32 text | None | Required |
| issuer | Service name or domain | None | Recommended |
| algorithm | SHA1, SHA256, SHA512 | SHA1 | Optional |
| digits | 6 or 8 | 6 | Optional |
| period | Seconds, such as 30 or 60 | 30 | Optional |
| counter | Whole number | None | Needed for HOTP |
Algorithm, Digits and Period Settings
Leave the three tuning settings alone unless your service uses something different. The default algorithm is SHA1, which every authenticator app supports, while SHA256 and SHA512 are read by fewer apps. The digits value controls code length, with 6 digits as the usual choice. The period sets the validity period of each code, and 30 seconds is what apps assume when it is missing.
An app stores these values at scan time. If you change them on your backend later, generated codes stop matching and the user has to re-enroll with a fresh code, so pick them once.
Why some apps ignore parameters
Some apps ignore non-default settings in the string and quietly fall back to the common hash algorithm and code length. Test every non-default value in each app your audience uses before you ship it.
Percent-Encoding the Label and Issuer
Everything outside the unreserved character set must be escaped with percent-encoding, following RFC 3986. A space becomes %20, an at sign becomes %40, and an ampersand becomes %26. The separator between issuer and account may stay literal or appear as %3A, and a tolerant consumer accepts both. Both fields must be percent-encoded, and the issuer parameter stays URL-encoded in its own query slot. A forgotten ampersand in a company name will cut the query string in half and corrupt the secret that follows it.
A label that needs escaping
An issuer called Dunmore & Pike becomes Dunmore%20%26%20Pike in both the label and the issuer parameter. A builder does this automatically, which is the main reason to use one instead of concatenating strings yourself.
Worked Example: Building a Complete TOTP URI
Suppose you run a service called Harbor Ledger and need to enroll [email protected]. Your server produced the Base32 secret MNSWIYLSFVWGC3TUMVZG4LJUGQ3TC, which is 29 characters long. You keep SHA1, six-digit codes and a 30-second step. The builder escapes the space in the issuer and the at sign in the account name, then joins everything:
otpauth://totp/Harbor%20Ledger:priya.nair%40harborledger.example?secret=MNSWIYLSFVWGC3TUMVZG4LJUGQ3TC&issuer=Harbor%20Ledger&algorithm=SHA1&digits=6&period=30Notice that the issuer appears in the label and again in the issuer parameter, with identical text. Since the last three settings equal the defaults, you could drop them and keep the string shorter.
The same entry as an HOTP string
For a counter-based entry, switch the type and add a counter. A hardware-token account for [email protected] with the secret ORUWIZJNOJUW4ZZNHEYDQMQ would read:
otpauth://hotp/Dunmore%20%26%20Pike:[email protected]?secret=ORUWIZJNOJUW4ZZNHEYDQMQ&issuer=Dunmore%20%26%20Pike&counter=0Using the Builder to Set Up a Network Admin from a URI String
Rosa Delgado, who runs the operations desk at a regional fiber provider called Kestrel Fiber, has to put a shared admin login behind two-factor codes before Friday's audit. She already has the secret her backend generated, OJSWYYLZFVTWC5DFFUYDGMJY, 24 characters long, and the login [email protected].
She types the issuer as Kestrel Fiber, pastes the secret, and leaves algorithm, digits and step on their defaults, the values RFC 6238 uses as its baseline. The result appears as she types:
otpauth://totp/Kestrel%20Fiber:noc-admin%40kestrelfiber.example?secret=OJSWYYLZFVTWC5DFFUYDGMJY&issuer=Kestrel%20FiberThe builder's string is 118 characters long. She checks three things before trusting it: the space in the name shows as %20 in both places, the at sign shows as %40, and the secret contains only the letters and the numbers 2 to 7 that the alphabet allows. All three hold.
She then pastes the same secret with the last letter changed to the number 1 as a test, and the builder flags it, because 1 is outside that alphabet. That confirms the field validates what it should. With the correct string she puts the code on screen, her teammate Tomas Reyes scans it with his phone, and the entry appears under the group name Kestrel Fiber.
The decision comes next: the generated link provisioned the entry, and the 6-digit code on his phone matches the code her backend computes for the same time step, so she records the setup as complete. Because the builder confirmed the string once, she reuses the identical name for the next two admin accounts rather than retyping it, which prevents the duplicate-looking entries she saw in an earlier rollout.
How to Use the Builder Step by Step
- Enter the issuer, keeping it short and identical across all of your accounts.
- Enter the account, usually the email address of the user.
- Paste the secret.
- Confirm the digits, period and algorithm.
- Press copy URI, or show QR to render a scannable code in the browser.
Checking a TOTP URI in Different Authenticator Apps
Once you have a finished string, test it where your users will. Google Authenticator reads the basic layout and shows the issuer and account together, while Microsoft's app and Authy follow the same layout with small differences in how they group entries. Apple's built-in password manager can read the same string and offer to attach a verification code to a saved login. Each app converts the shared secret into a rolling one-time password, so what you see on screen should match the code your backend expects at that moment.
Display name and grouping behavior
Most apps build the display name from the label, then use the issuer parameter to group related entries. If the two disagree, you may see two entries for one service. Compare how each consumer shows your issuer before you roll the link out to customers.
Rendering with a QR code generator
The builder's show QR option renders the finished string directly. If you prefer to produce the image yourself, any QR code generator can encode the same line, as long as it runs locally. Encode the whole string exactly as built and choose a high error-correction level so glare on a screen does not spoil reading.
Common Mistakes in a Hand-Written URI String
- Leaving a space or an at sign unescaped in the label, which is the most frequent cause of a broken import.
- Padding the Base32 secret with equals signs, which some apps reject.
- Adding a second separator inside the issuer or the account name.
- Writing the secret in lowercase, or with the numbers 0, 1, 8 or 9, none of which belong to the Base32 alphabet.
- Using different spellings of the service name in the label and in the issuer parameter.
Where the otpauth URI Scheme Stands as a Standard
The otpauth URI scheme is registered only provisionally with IANA, and a provisional entry in the IANA registry carries less weight than a full registration, so there is no single finished specification. Each competing draft specification proposes slightly different rules for issuer handling, which is why a builder that follows the widely deployed form is safer than one that follows a single draft. Google's Key URI Format, now archived, set the convention that deployed apps follow, and Yubico documents it as the layout authenticator apps use for every OATH credential. Several independent Internet-Draft documents try to turn that practice into a shared standard, written in ABNF and aiming at interoperability across vendors. Until one is adopted, producers should emit conservative strings and consumers should stay tolerant. Authenticator makers such as Microsoft and Authy read the same basic layout, which is why one string works in many places.
Security and Privacy When You Generate an OTP Link
The builder's output contains the shared secret in readable form, so privacy matters. Build it in the browser or on a machine you control, never in a tool that sends the secret elsewhere. Do not paste it into chat tools or tickets. Once a user has scanned the code, delete any copies you made. HMAC-based codes are only as strong as the secrecy of that value, and a leaked string means you must issue a new secret.
Also remember that a valid-looking link is not proof that an app accepted it. Always try the result with at least one real app before sending it to users.
If a secret ever leaks, issue a fresh one and build a new link; never reuse the old string, and log each enrollment event so you can spot unexpected setups.
Frequently Asked Questions
- What is an otpauth:// URI?
- It's the de facto standard URI scheme authenticator apps use for enrollment -- otpauth://TYPE/LABEL?secret=...&issuer=...&algorithm=...&digits=...&period=.... It was never a formal RFC, but Google Authenticator's original implementation became the shared convention every major app (Authy, Microsoft Authenticator, 1Password, Bitwarden) now supports.
- What's the difference between TOTP and HOTP?
- TOTP (time-based) generates a new code every fixed period (usually 30 seconds) based on the current time. HOTP (counter-based, the older of the two, defined in RFC 4226) generates a new code each time it's used, based on an incrementing counter both sides track. Almost all modern 2FA setups use TOTP; HOTP mainly appears in older hardware tokens.
- Why does the label include the issuer?
- Authenticator apps display the label to help users tell accounts apart. Prefixing it with 'Issuer:' (e.g. 'GitHub:[email protected]') lets the app group and label entries by service even if you have multiple accounts with the same username across different sites -- this tool does that automatically when you fill in the Issuer field.
- Do I need to change the algorithm, digits, or period?
- No -- leave them at the defaults (SHA1, 6 digits, 30-second period) unless a specific service's documentation tells you otherwise. Those are the universal defaults every authenticator app assumes when a URI omits them, which is why this tool only adds them to the output when you've changed them from default.
- Does this tool store or transmit my secret?
- No. The URI is built entirely in your browser from the values you type. Nothing is sent to a server, logged, or stored -- reload the page and everything is gone.