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_id values are unique only within a district and can overlap across districts.
  • A user removed from Clever for 300 or more days, or whose sis_id changes, 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 id for API calls. Do not use a role-level legacy_id as the user identifier.
  • student and contact roles 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_admin is district-wide and does not have a schools array.
  • Student and teacher roles have both school and schools; use school for primary-school logic.
  • Staff roles use schools and do not have a primary school field.
  • 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.

  1. Check whether the record exists in your database.
  2. Archive it if it exists.
  3. Ignore the 404 if it does not exist locally.
  4. Continue processing the remaining district records.

Check data freshness correctly

Do not confuse these district fields:

  • last_sync is the most recent time the district synced data into Clever.
  • last_clever_sync is 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

Logins 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:

ResponseAction
400, 401, 501Fix the request, authentication, or endpoint. Do not retry unchanged.
400 Invalid grantThe authorization code expired or was already exchanged. Check for duplicate code exchanges.
404Confirm the resource, sharing scope, token type, and request URL.
413Reduce limit to 10,000 or fewer.
429Wait for the rate-limit window to reset, then retry.
500, 502, 503Retry 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.

  1. Process the current response.
  2. Follow the response link with rel: next to retrieve the next range.
  3. 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.


Did this page help you?