For developers · one REST API

Build on the API the fkra apps run on.

The studio, the player, and the admin area read and write through one REST API. Your service calls the same endpoints for organizations, paths, modules, and progress.

zsh · fkra api
{ "statusCode": 200, "data": { "isRevoked": false, … } }
/api/v1200 OK
POST/public/user/login/credentialsign inPOST/shared/user/refreshrotate tokensGET/shared/user/profilecurrent userGET/user/learning/pathspublished pathsGET/public/certificate-verify/:codeverify a certificate
every app surface, same endpoints

If the apps can do it, the API can too

  • One key and secretEvery request carries x-api-key. Routes behind sign-in add a Bearer token.
  • One envelopeStatus code, message, metadata and data on every response.
  • A host per organizationEach organization has its own subdomain, and roles are read on every request.
  • Arabic and EnglishMessages come back in the language you ask for.
Three calls

Three calls, from sign-in to content

Sign in, read the current user, then list the learning paths.

01Sign in
RequestPOST/public/user/login/credentialx-api-keyx-captcha-token
accessTokenrefreshToken
Tokens issued
02Read the current user
RequestGET/shared/user/profilex-api-keyAuthorization
rolepermissions
Role and permissions returned
03List learning paths
RequestGET/user/learning/pathsx-api-keyAuthorization
enar
Titled in the locale you ask for
API reference

Every endpoint, header and error code in one place.

One REST API behind every fkra surface. Same objects, same permissions, same audit trail, whether the call comes from the studio, the player, or your own service.

docsv1 ProductChangelogGet an API key
Base URLhttps://<your-api-host>/api/v1Authx-api-key · BearerContentapplication/json

NoteEach snippet shows a request and the response it returns. Non-production deployments serve the full OpenAPI reference at /docs.

Quickstart

#

Send the email and password, get back an access token and a refresh token. The call also needs a solved CAPTCHA token. Accounts with two-factor on get a challenge instead of tokens.

login.sh
curl -X POST "https://<your-api-host>/api/v1/public/user/login/credential" \  -H "Content-Type: application/json" \  -H "x-api-key: $FKRA_KEY:$FKRA_SECRET" \  -H "x-captcha-token: $CAPTCHA_TOKEN" \  -d '{ "email": "amina@acme.edu", "password": "…", "from": "website" }'
POST/api/v1/public/user/login/credential200 OK
Body parameters
FieldTypeDescription
emailrequiredstringThe account's email address.
passwordrequiredstringThe account's password.
fromrequiredenumwebsite or mobile, depending on where the sign-in happens.

Authentication

#

Signed-in calls carry two credentials. The API key identifies your app and goes on every request; the access token identifies the user and goes on every route behind sign-in.

shell
curl "https://<your-api-host>/api/v1/shared/user/session/list" \  -H "x-api-key: $FKRA_KEY:$FKRA_SECRET" \  -H "Authorization: Bearer $ACCESS_TOKEN"
accessTokenAn ES256-signed JWT. Send it as Authorization: Bearer; expiresIn says how many seconds it lives.refreshTokenAn ES512-signed JWT, accepted only by the refresh endpoint. Each refresh rotates the pair, and the session keeps the expiry it got at sign-in.

CarefulThe secret is shown once, when an admin creates the key. Keep it on your server, and reset the key from /admin/system/api-keys.

Define your own module types

#

An admin registers a module type with two JSON schemas, one for its settings and one for its content. The studio builds its editing forms from them.

module-type.json
{  "key": "practice.branching_scenario",  "learnerAction": "Decide",  "configSchema": {    "properties": {      "branches": { "type": "array", "minItems": 2 },      "endings":  { "type": "array", "minItems": 2 },      "scoring":  { "enum": ["rubric", "points"] }    }  },  "bodySchema": {    "properties": { "persona": { "type": "string" } }  }}
Rendered in the studio
branching_scenarioSev-1 at 3am
personaon-call-engineerbranchesarrayendingsarrayscoringrubric
fkra/admin/content/module-typelive

Organizations, roles, and permissions

#

Each organization is its own tenant on its own subdomain. Protected routes check the user's role, then a permission written as a subject and an action, and both are read fresh on every request.

Tenantacme-academy.fkra.ai019f6a2e-…
Permission
learningModule · publishPublish modules
apiKey · createCreate and reset API keys
assessment · updateStart, answer, and submit assessments
Members, content, and activity in acme-academy are stored under its organization id./admin/system/roles · live

Arabic and English, built in.

#

Every surface ships in Arabic and English with right-to-left layout: content, dashboards, certificates.

Send x-custom-lang: ar and the message comes back in Arabic, with metadata.language set to match. Learner content routes read Accept-Language and report which locale they served, with a flag when they fell back to another.

ENReading our codebasev4.2 · approved
01Absorb02Connect03Evaluate04Practice
Readiness
ARقراءة قاعدة الكود لديناالإصدار 4.2 · معتمد
01استيعاب02ربط03تقييم04ممارسة
الجاهزية

Endpoints

#

Eight endpoints to start with, from sign-in to certificate checks. The fkra apps call the same ones. Open one to see what it needs and what it returns.

Requiresx-api-key · x-captcha-tokenReturnsUserLoginResponseDto
{ "isTwoFactorEnable": false, "tokens": { "tokenType": "Bearer", "expiresIn": 3600, … } }
Every other route, with its request and response schemas, is in the OpenAPI reference at/docs

Responses and paging

#

Every response carries a numeric status code, a message in the caller's language, request metadata, and the data. Paginated lists add their paging state to the metadata.

ParameterExampleMode
page1offset
perPage20offset · cursor
cursormetadata.nextCursorcursor
searchaminaoffset · cursor
orderBycreatedAt:descoffset · cursor
metadata on a cursor page
"metadata": {  "language": "en",  "path": "/api/v1/shared/user/session/list",  "version": "1",  "type": "cursor",  "perPage": 20,  "hasNext": true,  "hasPrevious": false,  "nextCursor": "eyJ…",  "orderBy": [{ "createdAt": "desc" }],  "availableOrderBy": ["createdAt", "updatedAt"]}

Errors and limits

#

Errors use the same envelope. The HTTP status names the kind of failure, and statusCode in the body is fkra's own code for the exact cause. Validation failures add an errors array with one entry per field.

4015100 · 5102The API key is missing, malformed, or expired, or its secret does not match.
4015120 · 5041The access token is missing or expired, or its session was revoked.
4035101No active API key matches.
4035063 · 5180The user's role or permissions do not cover this route.
4036104The user has not accepted the current terms of service and privacy policy.
4225030The body or query failed validation.
429429Too many requests from this client. Wait for Retry-After.
5035000The token could not be checked right now. Retry the request.
a validation error
HTTP/1.1 422 Unprocessable Entity {  "statusCode": 5030,  "message": "There are validation errors.",  "metadata": { "language": "en", "version": "1", … },  "errors": [    {      "key": "isNotEmpty",      "property": "password",      "message": "password cannot be empty."    }  ]}

Limits100 requests every 10 seconds, counted per client address, not per key. Past that the API answers 429 with a Retry-After header in seconds. Certificate checks have their own limit of 60 a minute.

Audit log

Every write leaves a record.

Sign-ins, content edits, role and API key changes, and issued certificates are logged with the user, the action, the IP address, and the device. Failed attempts are logged too, and admins can export role events as CSV.

GET /api/v1/admin/activity-log/listlive
14:02:11learningModuleUpdatedpolicy-v4-2amina@acme.edu14:02:48userLoginCredentialwebsite · 10.4.2.18noor@acme.edu14:07:44adminApiKeyCreatepartner-syncamina@acme.edu
Included

The rest of the platform

Every workspace gets all of it.

AnalyticsEngagement, readiness, and completion, live per teamNotificationsAchievements and certificates land the moment they are earnedDiscoverA storefront for everything your organization teachesCommunitiesPractice groups, events, polls, and module discussionsGamificationPoints, badges, streaks, and verifiable certificatesMedia timelineChapters, cues, bookmarks, and notes on every videoLearning pathsStages, prerequisites, and outcomes, sequenced by difficultyAssessmentsAn authoring wizard, timed runs, server-side grading
Questions

What engineers ask first.

How do I get an API key?

Admins create and reset keys in the admin area, under System, API keys. Talk to us first if your organization is not set up yet, and we will walk you through it.

What does every request need?

The key and its secret in x-api-key, joined by a colon. Routes behind sign-in add the access token as a Bearer header. Sign-in, sign-up and recovery also need a solved x-captcha-token.

How do I page through a list?

Lists take perPage, cursor and orderBy, and the response metadata carries the paging state for the next call.

What happens when I hit the rate limit?

The API answers 429 with a Retry-After header in seconds. Validation failures answer 422 with an errors array, one entry per field.

How do organizations map to the API?

Each organization has its own subdomain. Members, content and activity live under its organization id, and the role and permissions are read again on every request.

Can the API answer in Arabic?

Yes. Send x-custom-lang: ar and messages come back in Arabic. Learner content routes read Accept-Language and report which locale they served.

Connect your service to the API.

Tell us what you are building. We will walk you through keys, roles and your organization host.

Arabic and English · Keys and roles set up with you