Attendance API (Developers)
Link external attendance and members into Semis Growth LMS using our REST API.
The Semis Partner API connects external member and attendance systems to Growth LMS. Use it to submit attendance from instructor-led training, employee development events, or other organisational learning programmes. Stable external identifiers support consistent member matching and attendance reconciliation.
Who this is for
This reference is for integration developers. You need an authorised organisation integration key and a server-side application that can make HTTPS requests.
Base URL
All partner endpoints live under a single base path:
https://semis.reispar.com/api/v1/partner/
Responses are plain JSON. Every request must be made over HTTPS.
Authentication
Authenticate with a secret API key using a bearer token in the Authorization header.
Keys start with the prefix sk_.
Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keep your key secret
Treat your API key like a password. Store it in a secure secret manager, never commit it to source control, and never expose it in browser or mobile client code. If a key is leaked, ask an organisation owner to rotate it immediately.
Getting a key
An organisation Owner or Co-Owner creates keys from Organisation Settings → Integrations. When creating a key they choose which scopes (permissions) it has. You'll receive the secret once, at creation time — copy it somewhere safe.
Scopes
Each key is granted only the capabilities it needs. Requests to an endpoint fail with
403 if the key lacks the required scope.
| Scope | Allows |
|---|---|
members:read | Read member profiles |
members:write | Create and update members |
completions:read | Check attendance eligibility |
attendance:write | Record attendance |
Quick start
Get a key
Ask an organisation owner to create a key with the scopes you need.
Sync your members
Synchronise member records so attendance can be matched to the correct employee or learner.
Record attendance
Push attendance events as they happen. The endpoint is safe to retry.
Record a single attendance event:
curl -X POST "https://semis.reispar.com/api/v1/partner/attendance/" \
-H "Authorization: Bearer sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"external_event_id": "evt_1024",
"external_member_id": "emp_5567",
"status": "present",
"occurred_at": "2025-03-14T09:05:00Z"
}'
Endpoints
Record attendance
/api/v1/partner/attendance/scope: attendance:writeRecords an attendance event for a member at an event. This endpoint is idempotent —
sending the same external_event_id + external_member_id again updates the existing
record instead of creating a duplicate, so it's safe to retry.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
external_event_id | string | Yes | The event's ID in your system |
external_member_id | string | Yes | The member's ID in your system |
status | string | No | present (default), absent, late, or excused |
occurred_at | string (ISO 8601) | No | When the attendance happened |
raw_payload | object | No | Any extra data you want stored for reference |
Example response (201 Created, or 200 OK when updating)
{
"attendance_id": "0a5f...c31",
"external_event_id": "evt_1024",
"external_member_id": "emp_5567",
"status": "present",
"member_found": true,
"created": true
}
Recommended practice
member_found tells you whether the attendance was matched to a known Semis
member. If it's false, sync the member first (see below) and send the event
again to link them.
Sync a member
/api/v1/partner/members/scope: members:writeCreates or updates a single member. Match is by external_member_id, then email.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
external_member_id | string | Yes | The member's ID in your system |
email | string | No | Used to match existing members |
first_name | string | No | |
last_name | string | No | |
employee_id | string | No | |
department | string | No | |
job_title | string | No |
Add ?dry_run=true to preview what would change without writing anything.
Example response (201 Created for new, 200 OK for updates)
{
"external_member_id": "emp_5567",
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Obi",
"employee_id": "E-5567",
"job_title": "Engineer",
"departments": ["Engineering"],
"created": true
}
Held for review
To protect existing member records, some updates are held for an organisation admin to
approve. When that happens you'll get a 409 with
"status": "pending_review" and a review_id. This is expected —
an admin approves or rejects it inside Semis.
Sync many members at once
/api/v1/partner/members/batch/scope: members:writeCreate or update up to 200 members in one call. Each member is processed independently, so one failure never rolls back the others.
Request body
{
"members": [
{ "external_member_id": "emp_1", "email": "one@example.com", "first_name": "One" },
{ "external_member_id": "emp_2", "email": "two@example.com", "first_name": "Two" }
]
}
Example response (200 OK)
{
"total": 2,
"created": 1,
"updated": 1,
"blocked": 0,
"failed": 0,
"results": [
{ "index": 0, "external_member_id": "emp_1", "status": "created" },
{ "index": 1, "external_member_id": "emp_2", "status": "updated" }
]
}
Read a member
/api/v1/partner/members/{external_member_id}/scope: members:readReturns the current member record, or 404 if no matching active member exists.
Check eligibility
/api/v1/partner/eligibility/scope: completions:readCheck whether a member is eligible to attend an event (for example, has completed a prerequisite). Pass both IDs as query parameters:
/api/v1/partner/eligibility/?external_member_id=emp_5567&external_event_id=evt_1024
Example response
{
"eligible": true
}
Rate limits
Responses include rate-limit headers so you can pace your requests:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining |
X-RateLimit-Reset | When the window resets |
If you exceed a limit you'll get a 429 response with a retry_after value (in seconds).
Bulk loads
For large imports, use the batch endpoint (up to 200 members per call) instead of sending one request per member. It's faster and easier on rate limits.
Errors
Errors return a JSON body with a detail message and a standard HTTP status code.
| Status | Meaning |
|---|---|
400 | The request was invalid (missing or malformed fields) |
401 | Missing, invalid, or revoked API key |
403 | The key lacks the required scope |
404 | The requested resource wasn't found |
409 | Conflict — held for review, or a competing update |
429 | Rate limit exceeded — retry after retry_after seconds |
{
"detail": "external_member_id is required."
}
Recommended flow
Sync members first
Synchronise employee and learner records with
members/ormembers/batch/before submitting their attendance.Record attendance as it happens
Call
attendance/whenever someone checks in. Retry safely on failure.Reconcile
If
member_foundisfalse, sync that member and resend the attendance event to link it.
Need a key or higher limits? Contact your organisation owner or the Semis team.