Skip to content

API — Event ingestion

The ingest API receives learning events from Moodle (or other sources) and queues them for processing. It is the surface used by the Moodle plugin, but you can send events directly if you build your own integrator.

https://ingest.softsysanalytics.com

All ingest endpoints (except /health) require the header:

X-SSEA-Key: ssea_<32_hex_chars>

Keys are provisioned by SoftSys Solutions per tenant. See Security & privacy for details on format, expiry, and revocation.

Sends a single event. Useful for low-volume integrators or tests.

Ventana de terminal
curl -X POST https://ingest.softsysanalytics.com/v1/events \
-H "X-SSEA-Key: ssea_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"event_type": "quiz_submitted",
"user_id": "moodle_user_42",
"course_id": "moodle_course_7",
"module_id": "moodle_cm_13",
"timestamp": "2026-04-13T14:30:00Z",
"metadata": {
"attempt_number": 2,
"score_percent": 84.5,
"time_taken_seconds": 1200
}
}'

Success response — 202 Accepted

{
"ok": true,
"data": {
"queued": 1,
"event_id": "<deterministic_hash>"
}
}

The 202 code indicates the event was accepted and queued, but not yet processed. The dashboard may take a few minutes to reflect newly queued events.

Sends up to 500 events in a single request. Recommended for high-volume integrators or initial sync flows.

Ventana de terminal
curl -X POST https://ingest.softsysanalytics.com/v1/events/batch \
-H "X-SSEA-Key: ssea_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"event_type": "course_viewed",
"user_id": "u1",
"course_id": "c1",
"timestamp": "2026-04-13T10:00:00Z"
},
{
"event_type": "resource_viewed",
"user_id": "u1",
"course_id": "c1",
"module_id": "m1",
"timestamp": "2026-04-13T10:01:00Z"
}
]
}'

Response — 202 Accepted

{
"ok": true,
"data": {
"queued": 2,
"skipped": 0
}
}
  • queued: events accepted and queued.
  • skipped: events discarded by validation (wrong format, unsupported types). Does not raise an error if at least one event is accepted.

Used by the Moodle plugin to migrate historical data (users, courses, enrollments, activities, grades) to the SSEA backend. Not for event telemetry — use /v1/events for that.

This endpoint is intended for the Moodle plugin and its payload format is coupled to the plugin’s flow. If you build your own integrator, you likely want to send normal events via /v1/events.

Does not require authentication. Useful for external monitoring and connectivity tests from the Moodle server.

Ventana de terminal
curl https://ingest.softsysanalytics.com/health

Response — 200 OK

{
"ok": true,
"service": "ssea-ingest"
}

The payload follows a common core with optional fields per event type. See Event types for the full catalog.

{
"event_type": "string (one of the 15 supported types)",
"user_id": "string (required — Moodle user ID)",
"course_id": "string (optional — Moodle course ID)",
"module_id": "string (optional — course module ID)",
"timestamp": "string (ISO 8601 UTC, required)",
"context": {
"object_id": "string (optional)",
"context_level": "number (optional)"
},
"metadata": "object (optional — event-specific fields)",
"ip": "string (optional — hashed server-side before persisting)"
}

Notes:

  • event_type: must be one from the catalog. Unknown types are discarded.
  • timestamp: when the event happened at the source (not when you send it). ISO 8601 UTC format with Z (2026-04-13T14:30:00Z).
  • ip: if you send it, the ingest worker computes SHA-256(ip + salt) and discards the raw value before queuing. If you prefer not to send IP, the Moodle plugin has a no_ip option.
  • metadata: event-specific fields enriched by the plugin (e.g. score_percent for quiz_submitted). See catalog.
HTTPMeaningClient action
202 AcceptedEvent(s) accepted and queued.Continue normally.
400 Bad RequestInvalid or malformed JSON.Review the request body. Do not retry without fixing.
401 UnauthorizedMissing, invalid, or revoked API key.Check the X-SSEA-Key header. If keys were valid before, contact support to confirm status.
402 Payment RequiredDaily plan quota exceeded.Wait for reset (UTC midnight) or upgrade plan. See Plans & limits.
422 Unprocessable EntityValidation failed (e.g. required event_type missing, batch >500 events).Fix the payload. Do not retry without fixing.
429 Too Many RequestsRate limit exceeded (see Retry-After header).Wait the indicated time and retry with exponential backoff.
503 Service UnavailableWorker misconfigured (missing critical secret).Very rare — contact support.

All error responses follow the format:

{
"ok": false,
"error": "Human-readable message",
"code": "CODE_CONSTANT"
}

Common error codes:

codeHTTPMeaning
UNAUTHORIZED401Invalid or expired API key.
INVALID_JSON400Malformed JSON.
VALIDATION_ERROR422Required field missing or wrong format.
BATCH_TOO_LARGE422Batch with more than 500 events.
PLAN_LIMIT_EXCEEDED402Daily plan quota exhausted.
RATE_LIMITED429Too many requests in the current window.
  • Default: 1,000 requests per minute, per tenant.
  • Enterprise plans: up to 10,000 req/60s, contract-negotiated.
  • 429 responses include the Retry-After: <seconds> header.

The recommendation for clients is to implement exponential backoff: retry after 1s, 2s, 4s, 8s… with a reasonable cap (e.g. 5 retries). The Moodle plugin does this automatically.

  • Each event produces a deterministic ID based on a hash of its key fields.
  • If you resend the same event (for example, after a network timeout followed by retry), the processing worker detects the duplicate and does not count it twice.
  • This means you can safely retry 202 requests: you do not duplicate data.