Secure Remote Activation of Speedify Devices
This article describes a scalable remote device activation system for Speedify client devices.
The usual alternative is to sign in on the device with a real account email and password, which means every router in the field is holding a credential that unlocks your whole team. This flow avoids that.
How It Works
| Piece | Runs where | What it does |
|---|---|---|
| Speedify Teams API | Trusted host | Account-level operations, authenticated with your Speedify Teams API key in the x-api-key header |
speedify_cli |
Each device | Communication with the local Speedify daemon, including querying an activation code |
Local client devices generate activation codes; a trusted host on your infrastructure redeems it via the Speedify Team API.
Activation is idempotent per device. The daemon keeps its identity across restarts and reinstalls of the same install, so re-activating links the same deviceUUID rather than burning a second seat. That UUID becomes the handle you use later to rename or unlink the device.
What You'll Need
- A Speedify for Teams account with admin access.
- A Teams API key. Generate one in the admin console at my.speedify.com/api by clicking +, entering a description, and clicking Generate Key.
- A trusted host to hold that key: your workstation, a jump box, or a CI runner. This is the only machine that ever sees the key.
- SSH access to each device, key-based rather than password-based.
- Speedify installed and running on each device. See Install Speedify on Linux if you're starting from scratch.
- The email address of the team member the device should be linked to. It must already be on the team.
Fleet tip: rather than linking dozens of routers to a person, create a dedicated machine account and link them all to that. When you add a member with POST /v1/teams/members and include a password field, the account is created directly instead of triggering an invitation email. Omit password and the person gets an invite instead.
Step 1: Get an Activation Code From the Device
Ask the daemon on the device for a one-time code:
ssh $USER@$DEVICE /usr/share/speedify/speedify_cli activationcode
You'll get back something like this:
{"activationCode":"192989","activationUrl":"https://my.speedify.com/..."}
The code is short-lived and single-use. If you sit on it for a while before redeeming it, fetch a fresh one.
Step 2: Redeem the Code From Your Trusted Host
Send the code to the Teams API from the machine that holds your key. Keep the key in an environment variable so it never lands in your shell history or a script in version control.
The @ in the email address has to be URL-encoded as %40 , because the email sits in the URL path.
curl -X PATCH \
"https://api.speedify.com/v1/teams/members/email/fleet%40example.com/activate-device" \
-H "x-api-key: $SPEEDIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"activationCode":"192989"}'
A successful call returns 200 and the device's UUID:
{"deviceUUID":"2275583a-4587-4e5e-964f-fd4fae0508e2"}
Record that UUID. It's how you'll refer to this device from now on.
At the same moment, the daemon on the device transitions to LOGGED_IN on its own. You don't have to run anything else on the device.
Step 3: Verify the Device From Both Sides
Confirm the result on the device and in your account. Checking both catches the case where the API call succeeded but the daemon didn't come up.
On the device:
ssh admin@device-01 /usr/share/speedify/speedify_cli state
You want {"state":"LOGGED_IN"} . Anything else means the daemon isn't activated yet.
From your account:
curl "https://api.speedify.com/v1/teams/members/email/fleet%40example.com/devices" \ -H "x-api-key: $SPEEDIFY_API_KEY"
Look for your UUID in the list, and check that seenActivationSuccess is true on that entry. Routers also report "role":"router" , which is a quick way to tell them apart from client installs.
Rename or Unlink a Device Later
Once you have a UUID, two v2 endpoints handle day-to-day device management.
Give the device a readable name so your device list isn't a wall of UUIDs:
curl -X PATCH \
"https://api.speedify.com/v2/teams/members/fleet%40example.com/devices/$DEVICE_UUID" \
-H "x-api-key: $SPEEDIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"friendlyName":"warehouse-router-03"}'
Unlink a device that's been retired, sold, or stolen. This frees the seat and cuts the device off:
curl -X DELETE \ "https://api.speedify.com/v2/teams/members/fleet%40example.com/devices/$DEVICE_UUID" \ -H "x-api-key: $SPEEDIFY_API_KEY"
If a device is stuck holding an active session, DELETE /v2/teams/members/{email}/devices/{deviceUUID}/active resets the session without unlinking the device.
Where the Secrets Live
The point of this flow is that no single compromised device gives an attacker anything useful. Here's what each party actually holds:
| Secret or artifact | Trusted host | Device | Speedify cloud | Lifetime |
|---|---|---|---|---|
| Teams API key | Yes, in an env var | Never | Verifies it | Until revoked |
| Activation code | Passes through once | Originates here | Redeems it | Minutes, void after use |
| SSH private key | Yes | No | No | Rotate at will |
| SSH public key | Yes | In authorized_keys |
No | Not secret |
| Account password | No | No | Yes | Never used in this flow |
deviceUUID |
Logged | Implicit | Yes | Handle for rename and unlink |
What Happens If Something Goes Wrong
| Scenario | What you do about it |
|---|---|
| A device is stolen or rooted | There are no credentials on it to find. Unlink its UUID to evict it from the team. |
| An activation code is intercepted | Redeeming it can only add that one device to your own team, where it shows up immediately in the device list. |
| The trusted host is compromised | Revoke the key with DELETE /v2/teams/api-keys/{apiKey} and issue a new one. Already-activated devices keep working. |
| A key gets committed to a repo | Same fix: revoke and reissue. Then pin the new key to your egress IPs with PUT /v2/teams/api-keys/{apiKey}/allowed-ips . |
You can create keys with POST /v2/teams/api-keys , passing a description and, if you want the restriction from day one, an allowedIPs value with comma-separated addresses or CIDR ranges.
If Activation Fails
| Status | What it means | Fix |
|---|---|---|
400 |
The code is malformed or expired | Fetch a fresh code from the device and retry right away |
401 |
The API key is missing or invalid | Check that $SPEEDIFY_API_KEY is set and current. Rotate it if you suspect it leaked |
403 |
The key is valid but not allowed to do this | Check the key's role and its IP allow list. Device management needs an admin key |
404 |
That email isn't on your team | Add the member first with POST /v1/teams/members , then retry |
429 |
No seats left when adding a member | Increase seats, or unlink devices and remove members you no longer need |
500 |
Server error | Retry with backoff |
If the API returns 200 but the device never reaches LOGGED_IN , check that the daemon is actually running. speedify_cli version is the fastest liveness test.
Command Reference
On the Device
Every speedify_cli command returns JSON.
| Command | Purpose | Example output |
|---|---|---|
activationcode |
Generates a one-time activation code for this device | {"activationCode":"192989","activationUrl":"..."} |
state |
Reports the daemon state | {"state":"LOGGED_OUT"} then {"state":"LOGGED_IN"} |
version |
Liveness check and version | {"maj":16,"min":9,"bug":1,"build":12934} |
From the Trusted Host
Base URL is https://api.speedify.com , and every request carries the x-api-key header.
| Task | Method and path | Body |
|---|---|---|
| Activate a device | PATCH /v1/teams/members/email/{email}/activate-device |
{"activationCode":"192989"} |
| List a member's devices | GET /v1/teams/members/email/{email}/devices |
None |
| Rename a device | PATCH /v2/teams/members/{email}/devices/{deviceUUID} |
{"friendlyName":"warehouse-router-03"} |
| Unlink a device | DELETE /v2/teams/members/{email}/devices/{deviceUUID} |
None |
| Reset an active session | DELETE /v2/teams/members/{email}/devices/{deviceUUID}/active |
None |
| Create an API key | POST /v2/teams/api-keys |
{"description":"CI pipeline","allowedIPs":"10.0.0.0/8"} |
| Revoke an API key | DELETE /v2/teams/api-keys/{apiKey} |
None |
| Restrict a key to your IPs | PUT /v2/teams/api-keys/{apiKey}/allowed-ips |
{"allowedIPs":"203.0.113.4,10.0.0.0/8"} |
The full reference, including every endpoint not covered here, lives in the Speedify Teams API documentation.