Workflow SmartLink SSO API
Last updated 19 October 2026
A SmartLink is a signed link that registers a client for a workflow and signs them in to Money Alive in one click. Your own system makes one for each client, for example a button in your client portal. When the client opens it, their account is created if they don't have one, they join the workflow, and their videos open.
SmartLinks are switched on for your account by us. If you don't see SmartLink in the workflow builder, contact us.
Switch it on for a workflow
- Open the workflow. On the Onboarding card, press + and choose SmartLink.
- Switch the SmartLink on (1).
- Open Details and copy the Shared secret (2) and the Workflow App ID (3). You need both to make SmartLinks.

Keep the shared secret private, like a password. Anyone who has it can make SmartLinks for that workflow. Only use it on your server, never in a web page or an app a client can see inside.
The link
https://moneyalive.io/signable/token/login| Add | What it is | Needed |
|---|---|---|
appId |
The Workflow App ID | Yes |
firstName |
The client's first name | Yes |
surname |
The client's surname | Yes |
username |
The client's email address | Yes |
phone |
The client's phone number | No |
expires |
When the link stops working, as a Unix time in seconds | Yes |
token |
The signature, made as shown below | Yes |
When a link expires
Every SmartLink must say when it stops working. Set expires to a time in the future, at most 7 days ahead. After that time the link is refused, so a link that is forwarded or found later can't be used to sign in.
Make a fresh link each time a client clicks through, for example when they press the button in your portal. Then a short expiry, such as 10 minutes, works well and is the safest choice.
expires is signed along with the other details, so it can't be changed once the link is made.
Make the signature
Build the query string from the details, without
token, encoding each value for a web address. For example:appId=83&firstName=Jane&surname=Smith&username=jane%40example.com&expires=1798761600Encode that whole query string for a web address again.
=becomes%3D,&becomes%26, and the%40from step 1 becomes%2540:appId%3D83%26firstName%3DJane%26surname%3DSmith%26username%3Djane%2540example.com%26expires%3D1798761600Put the shared secret in front, and take the SHA-1 hash of the result. That's the
token.token = sha1(SHARED_SECRET + encoded_query_string)Add
&token=and the token to the end of the query string from step 1. Keep the other details in the same order you signed them in.
Worked example
With the shared secret example-shared-secret, the details above give this token:
bd4970905eab62283adf1105025cb092562be03eand this SmartLink:
https://moneyalive.io/signable/token/login?appId=83&firstName=Jane&surname=Smith&username=jane%40example.com&expires=1798761600&token=bd4970905eab62283adf1105025cb092562be03eUse this example to check your own code before you switch to your real secret. The example's expires is a fixed time, so the link itself won't sign anyone in; your real links use a time a few minutes ahead.
Code
// The client's details, in the order you'll send them
$params = [
'appId' => $appId,
'firstName' => $firstName,
'surname' => $surname,
'username' => $email,
'expires' => time() + 600, // 10 minutes from now
];
// Encode the details, then encode the whole string again before signing
$query = http_build_query($params);
$token = sha1($sharedSecret . urlencode($query));
$smartLink = 'https://moneyalive.io/signable/token/login?' . $query . '&token=' . $token;import hashlib
import time
from urllib.parse import urlencode, quote_plus
# The client's details, in the order you'll send them
query = urlencode({
'appId': app_id,
'firstName': first_name,
'surname': surname,
'username': email,
'expires': int(time.time()) + 600, # 10 minutes from now
})
# Encode the whole string again before signing
token = hashlib.sha1((shared_secret + quote_plus(query)).encode()).hexdigest()
smart_link = f'https://moneyalive.io/signable/token/login?{query}&token={token}'What happens when it's opened
- A new client gets an account and joins the workflow.
- A client who already has an account joins the workflow, if they're not already in it.
- Either way, they're signed in and taken straight to their videos.
If something is wrong, the link is refused with one of these messages:
| Message | What to check |
|---|---|
| Invalid token provided | That you encoded the query string a second time, and that the details are in the same order as when you signed them. |
| Missing expires from request | That the link includes expires, and that it's a whole number of seconds. |
| Link has expired | That expires is in the future when the client opens the link, and that your server's clock is right. |
| Link expires more than 7 days ahead | That expires is no more than 7 days from now. |