Clever Integration Anti-Patterns & Edge Cases
Avoid common Clever identifier, data-model, sync, SSO, API error, and pagination failures before building and launching your integration.
Use this reference to avoid common Clever integration failures before you build, test, or launch your app.
Use stable identifiers
Use the top-level Clever user id as the primary key when you link a Clever user to an account in your system.
Do not use email address, sis_id, or another district-managed identifier as the primary key:
- Clever does not verify email addresses, and some users do not have an email address.
sis_idvalues are unique only within a district and can overlap across districts.- A user removed from Clever for 300 or more days, or whose
sis_idchanges, can receive a new Clever User ID. Treat the resulting record as a new user.
For field definitions, see Users.
Handle multi-role users
A user can hold more than one role in a district. Inspect every role in the roles object before assigning permissions or routing the user.
- Use the top-level user
idfor API calls. Do not use a role-levellegacy_idas the user identifier. studentandcontactroles cannot be combined with other roles. For example, a person who is both a teacher and a contact is returned as two separate user records with separate Clever IDs.district_adminis district-wide and does not have aschoolsarray.- Student and teacher roles have both
schoolandschools; useschoolfor primary-school logic. - Staff roles use
schoolsand do not have a primaryschoolfield. - Clever SSO does not identify the role context a person intends to use. Define a default routing behavior for users with multiple roles.
Use a district-app token for bulk GET /v3.1/users requests. For an SSO-only integration, use the SSO bearer token with GET /v3.1/users/{id} to retrieve the signed-in user.
For implementation guidance, see Multi-Role Users in Clever.
Model roster data defensively
Clever data reflects district data models, which can vary by SIS. Avoid assumptions that records always follow a single-school, single-role, or complete-data model.
- Strings can contain any UTF-8 characters and have no enforced maximum length. If you need a limit, use 255 characters as a safe default.
- Email addresses are not normalized and may not have a valid email format.
- Students and teachers can belong to multiple schools, participate in sections outside their primary school, or have no sections.
- Contacts are not guaranteed. Their type varies by SIS, and the full contact name is stored in
last_name. - Courses and terms are often derived from section data. A course requires
course_name; a term requires at least one of term name, start date, or end date. - A deleted and recreated course or term receives a new Clever ID.
- Sections are the source of student-teacher associations. A section requires at least one student, can have one primary teacher and up to nine co-teachers, and has no student-count limit. Section names are not guaranteed unique, and section grade may not match every student’s grade.
For the full set of considerations, see Edge Cases.
Keep syncs resilient
Build your sync so one unavailable record does not stop processing for a district.
Handle unshared records
A 404 can be expected when a district shares data at the student level. For example, a section may reference students who are not shared with your app.
- Check whether the record exists in your database.
- Archive it if it exists.
- Ignore the
404if it does not exist locally. - Continue processing the remaining district records.
Check data freshness correctly
Do not confuse these district fields:
last_syncis the most recent time the district synced data into Clever.last_clever_syncis the most recent time data scoped to your app synced through Clever. This field requires App Sync Notifications.
If last_sync changes but last_clever_sync does not, your app may already have the newest data available to its connection.
Plan for holds and rollover
Clever’s hold system pauses large-scale changes until a district administrator approves them. It does not apply to sharing-setting changes. During the school year, deleting more than 10% of a record type in one sync pauses the sync for review; the threshold is relaxed during rollover periods.
During a term or school-year rollover, an SIS may update an existing section in place or create a new section with a new Clever ID. Support both patterns, including archiving and reactivating records when appropriate.
For more information, see Troubleshooting Your Sync and Data Changes and Rollover.
Handle SSO edge cases
Make code exchange idempotent
Do not exchange the same authorization code more than once. Authorization codes are single-use, and duplicate exchanges return invalid_grant.
Native iOS apps using Universal Links can deliver a callback through both Safari and the app. Record processed codes, prevent a second exchange, and log duplicate callbacks for troubleshooting.
Support Clever-initiated logins without state
stateLogins initiated from the Clever Portal or an Instant Login link do not include a state parameter by default. If your identity-management system requires state, detect the incoming request, restart the flow from your app, and include your own state value.
Avoid loops with multiple app configurations
If one login button routes users across multiple Clever app configurations, use return_unshared=true on attempts before the final app. Clever redirects an unauthorized user to your redirect_uri with an unauthorized-user error.
Your callback must handle an error response with no authorization code. Do not add return_unshared=true to the final app attempt, or users without access to any configuration can enter an infinite loop.
Protect shared student devices
To prompt Clever to reconfirm a student’s identity on a shared device, add confirmed=false to the authorization URL. This behavior applies only to student users.
Manage sessions in your app
Clever does not support Single Logout. Logging a user out of your app should not end their Clever session, because that interrupts their other Clever-connected tools.
If you need to identify district-admin impersonation events, request the optional loginas_by_admin=true redirect parameter from Clever Support.
For more guidance, see Clever Single Sign-On - Best Practices & Edge Cases.
Retry API requests correctly
Classify errors before retrying:
| Response | Action |
|---|---|
400, 401, 501 | Fix the request, authentication, or endpoint. Do not retry unchanged. |
400 Invalid grant | The authorization code expired or was already exchanged. Check for duplicate code exchanges. |
404 | Confirm the resource, sharing scope, token type, and request URL. |
413 | Reduce limit to 10,000 or fewer. |
429 | Wait for the rate-limit window to reset, then retry. |
500, 502, 503 | Retry with exponential backoff. |
Use exponential backoff for temporary failures: wait 1 second, then 2 seconds, then 4 seconds. Stop after five retries and try again later.
Clever allows 1,800 requests per minute per bearer token. Read the X-RateLimit-* response headers and check status.clever.com when you see broad failures.
An HTML response instead of JSON usually indicates a token/endpoint mismatch, such as calling an unsupported endpoint or using the wrong token type.
For the complete error table, see API Responses and Error Messages.
Use range-based pagination
Clever API pagination uses starting_after and ending_before, not offset or page-number parameters.
- Process the current response.
- Follow the response link with
rel: nextto retrieve the next range. - Continue until there is no next link.
The default response size is 100 records. The maximum limit is 10,000. Pagination and the limit parameter require a district bearer token from a Secure Sync integration; SSO user tokens cannot use them.
A boundary link can return an empty data array. Treat that as the end of the range, not an error.
For examples, see Pagination and Response Limits.
Updated about 1 hour ago

