Worksonar REST-API v1

Programmgesteuerter Zugriff auf Kampagnen, Antworten, Teilnehmer und Auswertungen. JSON, Bearer-Auth, Premium-Tarif. Diese Seite ist auch als Markdown verfügbar.

1. Einleitung

Die Worksonar-API ist eine schlanke, REST-orientierte HTTP-API. Sie spricht ausschließlich JSON und nutzt Bearer-Tokens zur Authentifizierung. Alle Daten sind mandantenspezifisch isoliert: ein Token kann nur auf die Daten des Mandanten zugreifen, für den er ausgestellt wurde.

Verfügbar im Premium-Tarif. Tokens erzeugen Sie unter Tenant-Portal → API.

2. Base-URL & Versionierung

UmgebungBase-URL
Diese Instanzhttps://beta.worksonar.de/api/v1
Hosted (Beispiel)https://app.worksonar.de/api/v1

Versionierung erfolgt über das URL-Präfix (/v1). Breaking Changes erscheinen ausschließlich in einer neuen Major-Version mit mindestens 6 Monaten Parallelbetrieb.

3. Authentifizierung

Jeder Request muss einen Bearer-Token im Authorization-Header tragen. Tokens haben das Format wsk_ + 32 base64-url-Zeichen.

Authorization: Bearer wsk_kpYHa3-Z9c1m...x9F

Token erzeugen Sie unter /tenant/api. Der Klartext-Token wird nur einmal angezeigt — danach kennt das System nur einen SHA-256-Hash. Vergessen Sie den Token, erzeugen Sie einen neuen und widerrufen den alten.

4. Scopes

Pro Token wählen Sie eine oder mehrere Scopes:

ScopeWas erlaubt
readAlle GET-Endpoints (Lesezugriff)
writePOST · PATCH · DELETE (Schreibzugriff)

Fehlt ein erforderlicher Scope, antwortet der Server mit 403 insufficient_scope.

5. Rate-Limits

Pro Token gelten standardmäßig 60 Requests / Minute in einem rollenden Fenster. Bei Überschreitung kommt:

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{"error":{"code":"rate_limited","message":"Rate limit exceeded (60 requests / minute)."}}

Höhere Limits sind im Premium-Tarif vertraglich verhandelbar.

6. Fehler-Format

Alle Fehler folgen einem einheitlichen Schema:

{
  "error": {
    "code":    "validation",
    "message": "Required: title, module.",
    "details": { "missing": ["title"] }
  }
}
HTTPTypische Codes
400validation
401unauthenticated · invalid_token · expired_token
402plan_required (kein Premium-Tarif)
403insufficient_scope · plan_restriction · plan_limit · tenant_inactive
404not_found
422anonymity_protection (n<5) · campaign_inactive
429rate_limited

7. Paginierung

List-Endpoints akzeptieren ?limit=N&offset=M und liefern:

{
  "items":  [ /* objects */ ],
  "total":  427,
  "limit":  50,
  "offset": 100
}

Defaults: limit=50 (Antworten/Kampagnen) bzw. 100 (Teilnehmer), Maximum 500 bzw. 1000.

8. Anonymitäts-Schutz

Der Endpoint /analysis/:campaign_id liefert keine aggregierten Ergebnisse, wenn nach Anwendung der Filter weniger als 5 Antworten übrigbleiben. Statt eines Teilergebnisses kommt:

HTTP/1.1 422
{"error":{"code":"anonymity_protection","message":"Less than 5 responses (n=3). Analysis withheld per anonymity rule n>=5."}}

Der /responses-Endpoint liefert dagegen alle Rohdaten — die Verantwortung für die Anonymisierung liegt dort beim Konsument.

9. /me

GET/api/v1/meread

Liefert Identifikation des Tokens und des zugehörigen Mandanten. Ideal zum Smoke-Testen des Tokens.

Beispiel

curl -H "Authorization: Bearer $WSK" https://beta.worksonar.de/api/v1/me

Antwort

{
  "tenant":   { "id":"6f…","name":"Mustermann Logistik AG","slug":"mustermann-logistik","industry":"Logistik & Transport","plan":"enterprise" },
  "token":    { "name":"CRM-Sync","prefix":"wsk_kpYHa3","scopes":["read","write"],"created_at":"2026-05-29 09:12:00","last_used_at":"2026-05-29 09:33:11" },
  "api_version":"v1"
}

10. /campaigns

GET/api/v1/campaignsread

Query-Parameter

moduleFilter: gbu_psych | benefits | bgm
statusFilter: draft | active | closed
limit, offsetPaginierung
curl -H "Authorization: Bearer $WSK" "https://beta.worksonar.de/api/v1/campaigns?status=active"
GET/api/v1/campaigns/{id}read
POST/api/v1/campaignswrite

Body

{
  "title":                 "GBU Q3 2026",            // erforderlich
  "module":                "gbu_psych",              // erforderlich
  "description":           "Quartals-Erhebung",
  "start_date":            "2026-07-01",
  "end_date":              "2026-08-15",
  "access_code":           "GBU-Q3-2026",            // optional, sonst auto-generiert
  "expected_participants": 150,
  "requires_login":        false
}

Status der neuen Kampagne ist immer draft. Aktivieren mit PATCH.

PATCH/api/v1/campaigns/{id}write

Update einer Kampagne. Erlaubte Felder: title, description, status, start_date, end_date, access_code, expected_participants.

curl -X PATCH -H "Authorization: Bearer $WSK" -H "Content-Type: application/json" \
  -d '{"status":"active"}' https://beta.worksonar.de/api/v1/campaigns/CID

11. /responses

GET/api/v1/responsesread

Query-Parameter

campaign_idAuf eine Kampagne filtern
moduleModul-Filter
department · locationSegment-Filter
from · toZeitraum (ISO 8601, YYYY-MM-DD oder vollständig)
limit, offsetPaginierung (max 500)
GET/api/v1/responses/{id}read
POST/api/v1/responseswrite

Antwort programmatisch einreichen (z. B. aus externem Befragungs-Tool).

Body

{
  "campaign_id":       "CID",
  "department":        "Lager",
  "location":          "Düsseldorf",
  "is_anonymous":      true,                // default: true
  "participant_email": null,                // nur wenn !anonymous
  "answers":           {                    // erforderlich
    "zd_1":  4, "zd_2": 3, "zd_3": 4, "zd_4": 4,
    "vollst_1": 3, "vollst_2": 2, "vollst_3": 3
  }
}

Die Kampagne muss status: "active" haben, sonst 422 campaign_inactive.

12. /analysis

GET/api/v1/analysis/{campaign_id}read

Vorab berechnete Auswertung für die ganze Kampagne. Filter über ?department=…&location=… möglich (Anonymitäts-Schwelle n≥5 gilt auch hier).

Antwort (GBU Psych, gekürzt)

{
  "campaign_id": "CID",
  "module":      "gbu_psych",
  "n":           126,
  "analysis": {
    "dimensions": [
      { "key":"zeitdruck","label":"Zeitdruck","type":"burden","mean":3.04,"n":504,"ampel":"red" },
      { "key":"arbeitsumgebung","label":"Arbeitsumgebung","type":"burden","mean":2.79,"n":1260,"ampel":"red" },
      …
    ],
    "findings": [ … ],
    "measures": [
      { "dimension":"Zeitdruck","ampel":"red","measure":"Aufgabenanalyse und Personalplanung prüfen" },
      …
    ]
  }
}

13. /participants

GET/api/v1/participantsread

Optional Filter: ?invite_status=pending|invited|completed.

POST/api/v1/participantswrite

Body

{ "email":"mitarbeiter@firma.de", "name":"Anna A.", "department":"Lager", "location":"Düsseldorf", "campaign_id":"CID" }

Nutzt das MA-Limit des Mandanten-Tarifs — Premium unbegrenzt, andere Pläne entsprechend.

DELETE/api/v1/participants/{id}write

Antwortet mit 204 No Content bei Erfolg.

14. /benchmarks

GET/api/v1/benchmarksread

Liefert Branchen-Benchmark-Werte. Default: Branche des Tokens-Mandanten.

Optional: ?industry=Logistik+%26+Transport&module=gbu_psych

15. End-to-End-Beispiele

Beispiel A — Antwort aus externem Tool importieren

# 1. Aktive Kampagne finden
curl -H "Authorization: Bearer $WSK" "https://beta.worksonar.de/api/v1/campaigns?status=active&module=gbu_psych" | jq '.items[0].id'

# 2. Antwort posten
curl -X POST -H "Authorization: Bearer $WSK" -H "Content-Type: application/json" \
  -d '{"campaign_id":"CID","department":"Vertrieb","location":"Berlin","answers":{"zd_1":4,"zd_2":3,...}}' \
  https://beta.worksonar.de/api/v1/responses

Beispiel B — Analyse für Dashboard ziehen

curl -H "Authorization: Bearer $WSK" \
  "https://beta.worksonar.de/api/v1/analysis/CID?department=Lager" | jq '.analysis.findings[] | select(.ampel=="red")'

Beispiel C — Mitarbeiter aus HR-System synchronisieren

# Existierende holen
existing=$(curl -s -H "Authorization: Bearer $WSK" "https://beta.worksonar.de/api/v1/participants?limit=1000" | jq -r '.items[].email')

# Neue anlegen
while read email name dept loc; do
  if ! grep -q "$email" <<< "$existing"; then
    curl -X POST -H "Authorization: Bearer $WSK" -H "Content-Type: application/json" \
      -d "{\"email\":\"$email\",\"name\":\"$name\",\"department\":\"$dept\",\"location\":\"$loc\"}" \
      https://beta.worksonar.de/api/v1/participants
  fi
done < mitarbeiter.tsv

16. Clients in 3 Sprachen

WSK="wsk_…"

# GET mit JSON-Pretty-Print
curl -s -H "Authorization: Bearer $WSK" https://beta.worksonar.de/api/v1/me | jq

# POST mit Body
curl -X POST -H "Authorization: Bearer $WSK" -H "Content-Type: application/json" \
  -d @campaign.json https://beta.worksonar.de/api/v1/campaigns
// Node 18+ (globales fetch)
const BASE = 'https://beta.worksonar.de/api/v1';
const WSK  = process.env.WSK;

async function ws(path, init = {}) {
  const res = await fetch(BASE + path, {
    ...init,
    headers: { 'Authorization': `Bearer ${WSK}`, 'Content-Type': 'application/json', ...(init.headers||{}) },
  });
  const data = await res.json().catch(() => null);
  if (!res.ok) throw Object.assign(new Error(data?.error?.message || res.statusText), { status: res.status, data });
  return data;
}

// Listen
const { items } = await ws('/campaigns?status=active');

// Posten
const newCamp = await ws('/campaigns', {
  method: 'POST',
  body: JSON.stringify({ title: 'Q3 2026', module: 'gbu_psych' }),
});
import os, requests

BASE = "https://beta.worksonar.de/api/v1"
WSK  = os.environ["WSK"]
H    = {"Authorization": f"Bearer {WSK}", "Content-Type": "application/json"}

def ws(method, path, **kw):
    r = requests.request(method, BASE + path, headers=H, timeout=30, **kw)
    if r.status_code >= 400:
        raise RuntimeError(f"{r.status_code} {r.json().get('error', {}).get('message', r.text)}")
    return r.json() if r.text else None

# Lesen
me = ws("GET", "/me")
print(me["tenant"]["name"])

# Schreiben
camp = ws("POST", "/campaigns", json={"title":"Q3 2026","module":"gbu_psych"})
ws("PATCH", f"/campaigns/{camp['id']}", json={"status":"active"})

17. Changelog

VersionDatumÄnderungen
v1.02026-05Erstveröffentlichung. Endpoints für me / campaigns / responses / analysis / participants / benchmarks. Bearer-Auth mit Read/Write-Scopes, 60 req/min Rate-Limit, n≥5 Anonymitätsschutz im Analysis-Endpoint.
Brauchen Sie Webhooks, Bulk-Import oder erweiterten Rate-Limit? Sprechen Sie uns an unter api@worksonar.de. Roadmap aktuell: Webhooks für Submit-Events, OAuth 2.0 Authorization Code Flow für Third-Party-Apps.