The school API
For a school's own systems to add and remove staff under the school account, for example a nightly sync from the staff OU in Google Workspace (setup steps). Only people the school adds are in it; students and anyone else stay out even when they share the school's email domain.
Base URL: https://justsignup.io/api/org/v1
Key: made on your school page (Dashboard → School → API access), shown once, revocable there at any time. Send it as Authorization: Bearer jsk_…. 300 calls a minute per key.
Sign-in: people you add sign in at justsignup.io with that address, with the emailed one-time link or with Google. Either way they land under the school; there is no password and no invitation to accept.
Errors: plain JSON, {"error": "…"}, with 400 for bad input, 401 for a bad or revoked key and 429 over the rate limit (with retryAfterSeconds).
Check the key
Returns the school's name, domain settings, member counts and the date the plan is paid through. A quick way to see a key works.
curl https://justsignup.io/api/org/v1 \
-H "Authorization: Bearer jsk_YOUR_KEY"List staff
Everyone under the school. Each member has an email, a role (admin or member), a status (active, or invited and not yet accepted), how they came in (api, invite, link, domain or owner), whether they have signed in yet, and since when.
curl https://justsignup.io/api/org/v1/members \
-H "Authorization: Bearer jsk_YOUR_KEY"Add staff
One person or many. Adding someone twice is harmless ("existing"). They are covered by the school's plan from that moment. There is no invitation to accept: the first time they sign in with that address, with the emailed link or with Google, they land under the school. Nothing is emailed to them by this call.
curl -X POST https://justsignup.io/api/org/v1/members \
-H "Authorization: Bearer jsk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"members":[{"email":"b.jones@example-school.org","name":"Brittney Jones"},{"email":"r.lee@example-school.org"}]}'Remove staff
Their account and pages stay theirs; the school simply stops covering them. The admin can't be removed through the API.
curl -X DELETE "https://justsignup.io/api/org/v1/members?email=r.lee@example-school.org" \
-H "Authorization: Bearer jsk_YOUR_KEY"Sync a whole list
Makes the API-managed membership match a list, for example everyone in your staff group. Everyone listed is a member afterwards. With removeMissing, people the API added earlier who are no longer listed are removed. People the admin invited by hand, or who joined by link or domain, and the admin, are never touched by a sync. Up to 5,000 addresses a call. Safe to run on a schedule.
curl -X POST https://justsignup.io/api/org/v1/members/sync \
-H "Authorization: Bearer jsk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"members":[{"email":"b.jones@example-school.org"},{"email":"r.lee@example-school.org"}],"removeMissing":true}'Response: { "added": 3, "kept": 194, "removed": 1, "details": { ... } }
Set up a nightly sync from Google Workspace
About ten minutes, no code to write. A small script in your own Google Workspace reads everyone in your staff OU once a night and tells JustSignup "these are our staff." People in the OU are covered by the school's plan; people who leave it, or whose Google account is suspended, are removed from the school at the next run. Nothing is emailed to anyone by the sync.
- 1Make a JustSignup API key
Sign in at justsignup.io and open School in the top nav. Open Settings at the bottom of the school page, then API access → New key. Copy the key; it starts with
jsk_and is shown once. If you lose it, revoke it there and make a new one. - 2Create the script
Go to script.google.com signed in to your school Google account (you need Workspace admin rights that include reading users) and click New project. Click "Untitled project" at the top left and name it "JustSignup staff sync". Delete everything in
Code.gs, paste the script below, and save (the disk icon).Code.gs, the whole file// JustSignup staff sync for Google Workspace. // Reads one organizational unit (OU) from your directory and makes the school account match it. // Setup steps: https://justsignup.io/help/school-api#workspace // Change OU_PATH to your staff OU. The API key goes in Project Settings > Script Properties, not here. var OU_PATH = "/Staff"; // your staff OU, exactly as shown under Directory > Organizational units; sub-OUs are included var BASE = "https://justsignup.io/api/org/v1"; function syncStaff() { var key = PropertiesService.getScriptProperties().getProperty("JUSTSIGNUP_API_KEY"); if (!key) throw new Error("Add JUSTSIGNUP_API_KEY under Project Settings > Script Properties first."); var members = []; var pageToken; do { var page = AdminDirectory.Users.list({ customer: "my_customer", query: "orgUnitPath='" + OU_PATH + "' isSuspended=false", maxResults: 500, orderBy: "email", pageToken: pageToken }); (page.users || []).forEach(function (u) { if (u.archived) return; // archived accounts are no longer staff members.push({ email: u.primaryEmail, name: u.name && u.name.fullName ? u.name.fullName : undefined }); }); pageToken = page.nextPageToken; } while (pageToken); if (members.length === 0) { throw new Error("No active users found in OU " + OU_PATH + ". Check OU_PATH; nothing was changed in JustSignup."); } var res = UrlFetchApp.fetch(BASE + "/members/sync", { method: "post", contentType: "application/json", headers: { Authorization: "Bearer " + key }, payload: JSON.stringify({ members: members, removeMissing: true }), muteHttpExceptions: true }); var status = res.getResponseCode(); var body = res.getContentText(); if (status !== 200) throw new Error("JustSignup returned " + status + ": " + body); var r = JSON.parse(body); Logger.log("Synced " + members.length + " staff from " + OU_PATH + ": added " + r.added + ", kept " + r.kept + ", removed " + r.removed); } // Optional: shows who JustSignup currently has under the school. Changes nothing. function listStaff() { var key = PropertiesService.getScriptProperties().getProperty("JUSTSIGNUP_API_KEY"); var res = UrlFetchApp.fetch(BASE + "/members", { headers: { Authorization: "Bearer " + key }, muteHttpExceptions: true }); Logger.log(res.getContentText()); } - 3Set your staff OU
Change the one line
var OU_PATH = "/Staff";to the path of the OU your staff are in. Find it in the Google Admin console under Directory → Organizational units. The name at the very top of that page is the root of your organization and is not part of the path: an OU shown asStaffunder the root is/Staff, and one nested asEmployees → Teachersis/Employees/Teachers. Paths are case-sensitive and start with/. OUs inside the one you name are included. - 4Put the API key in Script Properties
In the script editor click the gear icon, Project Settings, in the left sidebar. Under Script Properties click Add script property. Property:
JUSTSIGNUP_API_KEY. Value: thejsk_…key from step 1. Click Save script properties. The script reads it from here, so the key is never in the code. - 5Turn on the Admin SDK
Back in the editor (the <> icon), find Services in the left sidebar and click the + next to it. Choose Admin SDK API and click Add. Leave the identifier as
AdminDirectory. - 6Run it once by hand
Pick
syncStaffin the function dropdown at the top and click Run. The first run asks for permission: choose your school account, click Advanced, then Go to JustSignup staff sync (unsafe), then Allow. "Unsafe" only means your own script hasn't been through Google's app review. The Execution log at the bottom should end with a line likeSynced 42 staff from /Staff: added 42, kept 0, removed 0. In JustSignup, School → People now lists everyone from the OU. - 7Make it run every night
Click the clock icon, Triggers, in the left sidebar, then Add Trigger at the bottom right. Function:
syncStaff. Event source: Time-driven. Type: Day timer. Pick any hour, for example 2am to 3am. Failure notification: Notify me immediately. Save. From now on the OU is the source of truth for who is in the school account.
If something goes wrong
No active users found in OU: OU_PATH doesn't match. Check the spelling and capitalization under Directory → Organizational units. Nothing was changed.JustSignup returned 401: the key is wrong or was revoked. Make a new one on the school page and update the script property.AdminDirectory is not defined: the Admin SDK API service wasn't added (step 5).Not Authorized to access this resource/api: the Google account running the script can't read the directory. Run it as a super admin, or give that account an admin role with the Users → Read privilege.- Want to see what JustSignup has without changing anything? Run
listStaffinstead and read the execution log.
What a sync does, and doesn't
- It's an upsert. Everyone in the OU ends up a member. People the sync added earlier who are no longer in the OU (left, moved, suspended, archived) are removed on the next run. Adding someone who is already there changes nothing.
- It only removes people it added. People the admin invited by hand, people who joined by link, and the admin are never removed by a sync. Remove those from the school page.
- Nobody's pages are ever wiped. A removed person keeps their account and their sign-up pages, and published pages stay live. They lose the school's plan (their pages fall back to the free plan) and the school's templates.
- The OU is the only door, apart from invitations the admin sends by hand. Students, parents and alumni with school addresses can't join on their own. Keep "open the school to the whole domain" off in school settings.
- New staff get no email from the sync. They sign in at justsignup.io with their school Google account, or the one-time email link, and land under the school. Tell them in your own way.
- Several email domains are fine. The sync adds whatever addresses the OU returns, whichever of your organization's domains they are on.
Still stuck? Reply to any email from us, or use the Help button on your dashboard. A person reads every message.