Base URL https://signworkpro.com/api/v1 · JSON over HTTPS · requires a Sign Work Pro brokerage license.
How it works
Two calls from your server, then one redirect of the realtor’s browser. The realtor only ever sees a one-time link; your credentials and their email address stay between our two servers.
- Realtor · your appThe realtor clicks “Open Sign Work Pro” in your app.The link points at your own server (for example
/go/signworkpro), not at Sign Work Pro. - Your server → Sign Work ProYour server gets an access tokenwith your client key and passcode. Reuse it for up to 10 minutes.
- Your server → Sign Work ProYour server sends the realtor’s emailwith the access token, and gets back a one-time sign-in link. A realtor new to Sign Work Pro gets an account on your license.
- Your server → realtor’s browserYour server redirects the browser to the link.Sign Work Pro signs the realtor in and shows their dashboard.
Before you start
Sign Work Pro issues your brokerage two credentials:
| Credential | Looks like | Treat it as |
|---|---|---|
| Client key | swpk_… (37 characters) | An identifier. Not secret on its own, but keep it server-side with the passcode. |
| Passcode | swpp_… (53 characters) | A password. Shown once when issued; we only keep a hash of it. If it’s lost, ask for a new one. |
Keep both on your server. Never put them in a web page, a mobile app, a browser cookie or a URL. Anyone holding them can sign in any realtor at your brokerage.
Your brokerage must be on an active Sign Work Pro brokerage license. Realtors you sign in, and accounts created for new realtors, belong to that license. To get credentials, contact us.
Step 1 · Get an access token
Swap your client key and passcode for a short-lived bearer token. Cache it and reuse it until shortly before expires_in; you don’t need a new token for every realtor.
Request body
| Field | Type | Description |
|---|---|---|
client_key | string, required | Your client key. |
passcode | string, required | Your passcode. |
curl -X POST https://signworkpro.com/api/v1/sso/token \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"client_key": "swpk_…", "passcode": "swpp_…"}'
Response 200
{
"access_token": "Yk3w…",
"token_type": "Bearer",
"expires_in": 600
}
expires_in is in seconds. Errors: invalid_credentials (401), license_inactive (403). See Errors.
Step 2 · Request a sign-in link
Send the email address of the realtor who’s signed in to your app. Call this when they click, not when you render the page: the link it returns lasts 60 seconds.
Headers
| Header | Value |
|---|---|
Authorization | Bearer <access_token> |
Accept | application/json |
Request body
| Field | Type | Description |
|---|---|---|
email | string, required | The realtor’s email address, as verified by your app. Not case-sensitive. |
name | string, optional | The realtor’s full name, used only when a new account is created. Without it we make one from the email (jane.doe@… becomes “Jane Doe”). |
curl -X POST https://signworkpro.com/api/v1/sso/login \
-H "Authorization: Bearer Yk3w…" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"email": "jane.doe@harbourrealty.com", "name": "Jane Doe"}'
Response 200 existing realtor, or 201 account created
{
"url": "https://signworkpro.com/sso/login/Hq8v…",
"expires_in": 60,
"account_created": false
}
Step 3 · Send the realtor
Redirect the realtor’s browser to url with an HTTP 302. Sign Work Pro signs them in and opens their dashboard.
- The link works once and for 60 seconds. Request a fresh one on every click.
- Open it in the realtor’s own browser (a normal redirect or a new tab), not from your server; the sign-in is a browser session.
- If the link has been used or has expired, the realtor sees the Sign Work Pro sign-in page with a message to go back to your app and try again.
- Two-step sign-in is skipped for these sign-ins, because your app has already checked who the realtor is.
Accounts and seats
Each sign-in matches the email to a Sign Work Pro account. What happens depends on that account:
| The email belongs to… | Result |
|---|---|
| An active realtor at your brokerage | Signed in. 200 |
| Nobody yet | A new Agent account is created on your license, if a seat is free, then signed in. 201 If every seat is taken: seats_full. 409 |
| A realtor your broker invited who hasn’t finished joining | Their account is activated and they’re signed in. 200 |
| A suspended realtor at your brokerage | account_suspended. 403 |
| An account outside your brokerage | email_in_use. The account isn’t changed or moved. 409 |
New accounts start with document sharing to the broker turned off, the same as a realtor who joins with your license key. A realtor who later wants to sign in to Sign Work Pro directly can set a password with “Forgot password”.
Errors
Errors come back as JSON with a stable code for your logic and a message written for the realtor. You can show message as it is.
{
"code": "seats_full",
"message": "All 40 seats on Harbour Realty's Sign Work Pro license are in use, so we can't add you yet. Ask your broker to add more seats."
}
| Status | Code | Meaning and what to do |
|---|---|---|
| 401 | invalid_credentials | Wrong client key or passcode, or the credentials were revoked. Check your configuration; don’t retry in a loop. |
| 401 | invalid_token | Access token missing, wrong or expired. Get a new one from step 1 and retry once. |
| 403 | license_inactive | Your brokerage’s license has ended or been revoked. Contact Sign Work Pro. |
| 403 | account_suspended | The realtor’s account is suspended. Their broker can reactivate it in Sign Work Pro. |
| 409 | seats_full | A new realtor can’t be added because every seat on the license is in use. |
| 409 | email_in_use | The email has a Sign Work Pro account outside your brokerage. |
| 409 | account_unavailable | The account exists but can’t be signed in to yet (for example, an unconfirmed self sign-up). Contact Sign Work Pro. |
| 422 | — | A field is missing or invalid. The body has message and an errors object keyed by field. |
| 429 | — | Too many requests. Wait for the number of seconds in the Retry-After header. |
Full examples
Each example is the handler behind your app’s “Open Sign Work Pro” link. It caches the access token, requests a link for the signed-in realtor and redirects.
PHP (Laravel)
// routes/web.php — credentials live in .env (SWP_CLIENT_KEY, SWP_PASSCODE)
Route::get('/go/signworkpro', function (Request $request) {
$base = 'https://signworkpro.com/api/v1/sso';
$token = Cache::remember('swp_sso_token', 540, fn () => Http::acceptJson()
->post("$base/token", [
'client_key' => config('services.swp.client_key'),
'passcode' => config('services.swp.passcode'),
])->throw()->json('access_token'));
$response = Http::acceptJson()->withToken($token)->post("$base/login", [
'email' => $request->user()->email,
'name' => $request->user()->name,
]);
if ($response->status() === 401) {
Cache::forget('swp_sso_token'); // expired: the next click gets a new token
}
if ($response->failed()) {
return back()->with('error', $response->json('message', 'Sign Work Pro is unavailable. Try again.'));
}
return redirect()->away($response->json('url'));
})->middleware('auth');
Node.js (Express)
// Credentials live in environment variables: SWP_CLIENT_KEY, SWP_PASSCODE
const BASE = 'https://signworkpro.com/api/v1/sso';
let cached = { token: null, expiresAt: 0 };
async function accessToken() {
if (cached.token && Date.now() < cached.expiresAt) return cached.token;
const res = await fetch(`${BASE}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify({ client_key: process.env.SWP_CLIENT_KEY, passcode: process.env.SWP_PASSCODE }),
});
if (!res.ok) throw new Error((await res.json()).message);
const body = await res.json();
cached = { token: body.access_token, expiresAt: Date.now() + (body.expires_in - 60) * 1000 };
return cached.token;
}
app.get('/go/signworkpro', requireLogin, async (req, res) => {
const r = await fetch(`${BASE}/login`, {
method: 'POST',
headers: {
Authorization: `Bearer ${await accessToken()}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({ email: req.user.email, name: req.user.name }),
});
const body = await r.json();
if (r.status === 401) cached = { token: null, expiresAt: 0 };
if (!r.ok) return res.status(r.status).render('error', { message: body.message });
res.redirect(302, body.url);
});
Python (Flask)
# Credentials live in environment variables: SWP_CLIENT_KEY, SWP_PASSCODE
import os, time, requests
from flask import redirect, render_template
from flask_login import login_required, current_user
BASE = "https://signworkpro.com/api/v1/sso"
_cache = {"token": None, "expires_at": 0}
def access_token():
if _cache["token"] and time.time() < _cache["expires_at"]:
return _cache["token"]
r = requests.post(f"{BASE}/token", timeout=10, headers={"Accept": "application/json"}, json={
"client_key": os.environ["SWP_CLIENT_KEY"],
"passcode": os.environ["SWP_PASSCODE"],
})
r.raise_for_status()
body = r.json()
_cache.update(token=body["access_token"], expires_at=time.time() + body["expires_in"] - 60)
return _cache["token"]
@app.get("/go/signworkpro")
@login_required
def open_sign_work_pro():
r = requests.post(f"{BASE}/login", timeout=10,
headers={"Authorization": f"Bearer {access_token()}", "Accept": "application/json"},
json={"email": current_user.email, "name": current_user.name})
if r.status_code == 401:
_cache["token"] = None
if not r.ok:
return render_template("error.html", message=r.json().get("message")), r.status_code
return redirect(r.json()["url"], code=302)
Security checklist
- The client key and passcode are stored on your server only, in environment variables or a secrets manager.
- Only send the email of the realtor who is signed in to your app right now, and only if your app has verified that address.
- The “Open Sign Work Pro” link points at your server and requires the realtor to be signed in to your app.
- Sign-in links are requested on click and never logged, emailed or stored.
- All calls use HTTPS. Don’t turn off certificate checks.
- If you think the passcode has leaked, tell Sign Work Pro straight away. We revoke it, which stops its tokens and any unused links immediately, and issue a new one.
Rotating the passcode. A brokerage can hold more than one set of credentials. Ask for a new set, switch your server over, then ask us to revoke the old one. There’s no downtime.
Limits and timings
| What | Value |
|---|---|
| Access token lifetime | 600 seconds (10 minutes) |
| Sign-in link lifetime | 60 seconds, single use |
POST /sso/token | 60 requests per minute per IP address |
POST /sso/login | 300 requests per minute per IP address |
| Opening sign-in links | 60 per minute per IP address |
Sign Work Pro keeps an audit record of every sign-in, every account created and every failed credential check made through this API. For credentials or help with an integration, contact us.