If your customers already log in to your own app, single sign-on (SSO) lets them use Tender without a second account or password. Your app tells Tender who someone is with a multipass: a small encrypted message holding their email address and name. Tender logs them in, creating their Tender profile the first time.
Single sign-on is set up by your developers, and depends on your plan.
Turning it on
- Go to Account & Settings › Extras and click Single Sign-On (under Advanced) in the sidebar.
- Click On in the Single Sign-On box.
- Fill in the box:
- Login URL: your app's login page. Tender's Log in links go there instead of Tender's own login page, with a
toparameter added saying which Tender page to come back to, for examplehttps://app.example.com/login?to=https://help.example.com/discussions. Customers can no longer log in or sign up on Tender itself. - Logout URL (optional): where Tender sends people after they log out of Tender, such as your app's logout page.
- Login URL: your app's login page. Tender's Log in links go there instead of Tender's own login page, with a
- Click Save your changes.
The box also shows the two values your developers need: the Site key (your site's name in its tenderapp.com address) and the SSO API key. Keep the SSO API key secret, like a password: anyone with it can log in to your Tender as anyone.

Each click on On (even when single sign-on is already on) makes a new SSO API key, and so does turning it off and on again. Multipasses made with the old key stop working, so update your app when that happens.
How a login works
- A customer clicks Log in on Tender, and lands on your Login URL with
?to=<the Tender page>. - Your app logs them in as usual (or sees they already are).
- Your app makes a multipass for them and sends them back to the
toaddress withsso=<multipass>added, for examplehttps://help.example.com/discussions?sso=.... - Tender reads the multipass, logs them in, and shows the page.
You can also link straight to any Tender page with ?sso=<multipass> from inside your app, so customers who are logged in to your app arrive on Tender already logged in.
Your support staff can use single sign-on too, if they have accounts in your app with the same email address. They can also keep logging in with their Tender password at your site's /login page.
What goes in a multipass
A multipass is a JSON object:
{
"email": "jane@example.com",
"name": "Jane Doe",
"unique_id": "42",
"expires": "2026-10-09T12:05:00Z"
}
| Key | |
|---|---|
email |
The customer's email address. Required. |
expires |
When this multipass stops working. Required: use a time a few minutes ahead, in UTC. ISO 8601 (2026-10-09T12:05:00Z) is simplest. |
name |
The name Tender shows. display_name, username or login also work. |
unique_id |
The customer's id in your app. Tender uses it to recognise them, so they keep the same Tender profile even if their email address changes. Without it, Tender uses the email address. guid also works. |
alternate_id |
Used to find the customer when no one has their unique_id yet, for example their email address when you start sending unique_ids. |
trusted |
true to never check this customer's comments for spam. |
beta, important |
true or false to give the customer beta or important access. |
company_name or company_id |
Puts the customer in that company, creating it if the name is new (on plans with companies). |
to |
An address on your Tender site to send the customer to after logging in. |
If your app has test or staging copies, add a prefix to their unique_ids (staging-42) so they don't collide with your real customers.
Extra information about the customer
Any other keys are kept as custom fields on the customer's profile: a plan name, an account number, a link to them in your admin. For example:
{
"email": "jane@example.com",
"name": "Jane Doe",
"unique_id": "42",
"plan": "Business",
"admin": "https://app.example.com/admin/users/42",
"expires": "2026-10-09T12:05:00Z"
}
When the customer starts a discussion, your support staff see these fields in a Private information (visible only to supporters) box on the discussion. Values that are web addresses become links. Customers never see them.
Encrypting it
The multipass is encrypted with AES so that only Tender can read it, and no one can make one without your SSO API key:
- Key: take the SHA-1 digest of your SSO API key followed by your Site key (
SHA1(sso_api_key + site_key), as raw bytes) and keep the first 16 bytes. - Encrypt the JSON with AES-128 in CBC mode, with that key, an initialization vector of 16 zero bytes, and PKCS#7 padding (the default in most libraries).
- Encode the result in URL-safe Base64:
-instead of+,_instead of/, and no=at the end.
The result can go in a URL as it is.
Ruby
require 'openssl'
require 'base64'
require 'json'
require 'time'
SITE_KEY = 'yoursite' # your Site key
SSO_KEY = ENV.fetch('TENDER_SSO_KEY') # your SSO API key
def tender_multipass(data)
cipher = OpenSSL::Cipher.new('aes-128-cbc')
cipher.encrypt
cipher.key = OpenSSL::Digest::SHA1.digest(SSO_KEY + SITE_KEY)[0, 16]
cipher.iv = "\0" * 16
encrypted = cipher.update(data.to_json) + cipher.final
Base64.urlsafe_encode64(encrypted).delete('=')
end
token = tender_multipass(
email: 'jane@example.com',
name: 'Jane Doe',
unique_id: '42',
expires: (Time.now.utc + 300).iso8601
)
puts "https://yoursite.tenderapp.com/?sso=#{token}"
PHP
<?php
$site_key = 'yoursite'; // your Site key
$sso_key = getenv('TENDER_SSO_KEY'); // your SSO API key
function tender_multipass($data, $site_key, $sso_key) {
$key = substr(sha1($sso_key . $site_key, true), 0, 16);
$iv = str_repeat("\0", 16);
$encrypted = openssl_encrypt(json_encode($data), 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $iv);
return rtrim(strtr(base64_encode($encrypted), '+/', '-_'), '=');
}
$token = tender_multipass([
'email' => 'jane@example.com',
'name' => 'Jane Doe',
'unique_id' => '42',
'expires' => gmdate('c', time() + 300),
], $site_key, $sso_key);
echo "https://yoursite.tenderapp.com/?sso=" . $token . "\n";
Node.js
const crypto = require('crypto');
const SITE_KEY = 'yoursite'; // your Site key
const SSO_KEY = process.env.TENDER_SSO_KEY; // your SSO API key
function tenderMultipass(data) {
const key = crypto.createHash('sha1').update(SSO_KEY + SITE_KEY).digest().subarray(0, 16);
const cipher = crypto.createCipheriv('aes-128-cbc', key, Buffer.alloc(16));
const encrypted = Buffer.concat([cipher.update(JSON.stringify(data)), cipher.final()]);
return encrypted.toString('base64url');
}
const token = tenderMultipass({
email: 'jane@example.com',
name: 'Jane Doe',
unique_id: '42',
expires: new Date(Date.now() + 5 * 60 * 1000).toISOString(),
});
console.log(`https://yoursite.tenderapp.com/?sso=${token}`);
Checking a multipass
Once single sign-on is on, SSO Debugger appears in the Extras sidebar (the SSO token debug tool link on the Single Sign-On page goes there too). Paste a multipass under Debug a MultiPass Token and click Decode token to see what Tender reads from it, or why it can't.
The usual problems:
- "The MultiPass token has expired."
expiresis missing or in the past. Check your server's clock and that the time is in UTC. - "The MultiPass token was not able to be decrypted." The key is wrong (check the SSO API key and Site key, and their order), or the multipass was changed on the way, for example by escaping it twice.
- "The decrypted MultiPass token is not valid JSON." The data you encrypted isn't a JSON object.
The support widget
The support widget on your own website takes a multipass too, in its sso option, so customers using it are logged in.
Cookie login (older)
Before multipasses, Tender could log people in from cookies your app sets, when Tender runs on a subdomain of your app's domain (your app at example.com, Tender at help.example.com). It still works, but a multipass is simpler and safer; use it for anything new.
Your app sets these cookies on .example.com when someone logs in:
| Cookie | |
|---|---|
tender_email |
Their email address |
tender_expires |
When the login expires, in seconds since 1970 (1893456000) |
tender_name |
Their name (optional) |
tender_hash |
The HMAC-SHA1 (hex) of host/email/expires, or host/email/expires/name with a name, made with your SSO API key (single sign-on must be on) |
tender_<anything> |
Extra information, shown like a multipass's extra keys |
host is your site's custom domain (help.example.com) or its yoursite.tenderapp.com address. To check your code: with the key monkey, the string help.example.com/jane@example.com/1893456000 gives 4b3a6af4b05feb08183c4aa81b818557961bae3b.
Google Sign-In
The same page can also let people log in to Tender with their Google account. Create an OAuth 2.0 client ID in the Google Cloud console, add the addresses the page lists as authorized JavaScript origins, and paste the client ID into OAuth Client ID under Google Sign-In.
