Skip to main content

MagicINFO REST API: sign in with TOTP and get a token

Send POST /MagicInfo/restapi/v2.0/auth with a JSON body containing username, password and a fresh six-digit totp code, then pass the returned token in an api_key header on every other call.

The request​

curl -X POST "https://your-server:7002/MagicInfo/restapi/v2.0/auth" \
-H "Content-Type: application/json" \
-d '{"username":"api-user","password":"<password>","totp":"123456"}'
  • The field names are username and totp. Older examples use id and otpValue. That shape is rejected, and the error it produces (401005) looks like a token problem.
  • For an account with two-step verification, a sign-in without totp returns 401. On current MagicINFO 9 builds, plan for every account to need it.

The response​

A successful sign-in returns, among other fields:

{ "token": "eyJhbGciOiJIUzUxMiJ9…", "refreshToken": "eyJ…", "userId": "api-user" }

Read token and refreshToken from the top level, and fall back to an items object if your server version nests them there.

Using the token​

Send the token on every call in a header named api_key:

curl "https://your-server:7002/MagicInfo/restapi/v2.0/ums/users/me" -H "api_key: <token>"

This is not the standard Authorization: Bearer header, and adding a Bearer prefix breaks the token.

LifetimeWhen it runs out
token30 minutes (1800 s)calls return 401003
refreshToken7 days (604800 s)calls return 401004
  • In our tests the server also accepts the refreshToken in the api_key header for its full seven days. Our own long-running scripts use it that way. The alternative is to sign in again with a fresh code whenever a call returns 401.
  • The API's published request model also lists a refresh grant (grantType "refresh_token" with a refreshToken field). We have not tested it, so treat it as untested until you have.
  • Signing in again does not revoke earlier tokens, so two scripts can use the same account.
  • To sign out, send DELETE /MagicInfo/restapi/v2.0/auth with the api_key header.

Generating the TOTP code in a script​

The code is a standard time-based one-time password (RFC 6238): a Base32 secret, HMAC-SHA1, a 30-second step and 6 digits. Any TOTP library works. This version uses only the Python standard library:

import base64, hashlib, hmac, struct, time

def totp(secret_b32: str) -> str:
s = secret_b32.replace(" ", "").upper()
key = base64.b32decode(s + "=" * (-len(s) % 8))
counter = struct.pack(">Q", int(time.time()) // 30)
digest = hmac.new(key, counter, hashlib.sha1).digest()
offset = digest[-1] & 0x0F
code = struct.unpack(">I", digest[offset:offset + 4])[0] & 0x7FFFFFFF
return f"{code % 1_000_000:06d}"
  • Generate the code immediately before the call. If it is rejected (401013) near the end of a 30-second window, wait for the next window and retry once. Failed codes count towards the account lockout (401007).
  • The machine running the script needs an accurate clock.
  • Where the secret comes from: the account's two-step enrolment. For a new API user, the first sign-in with only a password returns 401011, and the reply carries the enrolment data (errorDetails.otpAuthInfo.secretKey), just as the web interface shows a QR code. Store that secret as carefully as the password, then send one sign-in with a totp code generated from it to complete the enrolment. Every password-only attempt generates a new secret, so always use the latest one.

The usual cause of 401005 "Token is invalid"​

401005 means the server could not read the token at all, not that it was revoked. In our investigations the cause was always on the client side:

  • a Bearer prefix, or quotes around the value
  • a byte-order mark or line ending added when the token was saved to a file or piped between programs (Windows PowerShell 5.1 does both)
  • one character lost when copying
  • an old token kept in a variable
  • the outdated id / otpValue request shape

Sign in fresh, send the token exactly as received, and the call should succeed. An expired token gives 401003, not 401005.

How to confirm it worked​

GET /MagicInfo/restapi/v2.0/ums/users/me with your api_key header returns HTTP 200 and the signed-in user. After 30 minutes, the same call with the short-lived token returns 401003, which proves your renewal logic is needed and working.

Checked against MagicINFO Server 21.1070 behaviour, October 2026. Behaviour can differ between server builds. Give API integrations their own user account with only the permissions they need, and keep its password and TOTP secret out of source code.