Schnittstelle · Anmeldung: Schlüssel je Firma
Kunden-API: Beschäftigte, Objekte, Zeiten, Abwesenheiten
Kurz gesagt
Die Kunden-API liegt unter /api/v1 und gibt Zugriff auf Beschäftigte, Objekte, Abwesenheiten und die ausgewertete Arbeitszeit einer Firma. Angemeldet wird mit einem Schlüssel im Authorization-Kopf, erzeugt in der Anwendung unter Verwaltung, API. Zeitwerte sind ganze Minuten, Datumsangaben JJJJ-MM-TT. Die maschinenlesbare Beschreibung nach OpenAPI 3.1 ist ohne Schlüssel abrufbar.
Zugriff auf Beschäftigte, Objekte, Abwesenheiten und ausgewertete Arbeitszeit einer Firma. Basis-Adresse ist https://app.casaclock.de.
Maschinenlesbare Beschreibung: GET /api/v1/openapi.json nach OpenAPI 3.1, ohne Schlüssel abrufbar. Ein Entwickler kann seinen Client daraus erzeugen, bevor der Zugang steht.
Schlüssel
Ein Schlüssel wird in der Anwendung unter Verwaltung, API erzeugt und dort auch widerrufen. Er gehört zu genau einer Firma; eine firmenübergreifende Abfrage gibt es nicht. Der Klartext ist nur im Moment der Erzeugung sichtbar, gespeichert wird der SHA-256-Hash.
Authorization: Bearer cc_live_…Beim Erzeugen werden drei Dinge festgelegt:
| Einstellung | Wirkung |
|---|---|
| Rolle EMPLOYEE / SUPERVISOR / ADMIN | bestimmt die Sicht auf Personaldaten |
| Schreiben erlauben | ohne dieses Recht sind POST, PATCH und DELETE gesperrt |
| Laufzeit | leer bedeutet unbefristet, sonst Ablauf nach N Tagen |
Schreibende Aufrufe verlangen beides: Rolle ADMIN und gesetztes Schreibrecht.
Ein ungültiger Schlüssel liefert immer dieselbe 401 mit derselben Meldung, gleich ob er unbekannt, widerrufen oder abgelaufen ist. Wer einen Schlüssel rät, soll nicht erfahren, ob er nur abgelaufen ist.
Regeln, die überall gelten
- Zeitwerte sind ganze Minuten. Dezimalstunden rechnet das System, das daraus Geld macht.
- Datum ist JJJJ-MM-TT, Zeitpunkte sind ISO 8601 mit Zeitzone. Der Betrieb rechnet in Europe/Berlin.
- Fehler haben immer dieselbe Form:
errorist für Programme,messagefür Menschen. - Listen liefern
gesamtund nehmenlimit(Standard 100, höchstens 500) undoffset. - Personen lassen sich per Kennung oder E-Mail-Adresse ansprechen, Objekte per Kennung oder
ref:REFERENZ. - Antworten tragen
Cache-Control: no-storeund erlauben CORS von jeder Herkunft; die API kennt keine Cookies, nur Schlüssel.
Endpunkte im Überblick
| Methode | Pfad | Zweck |
|---|---|---|
| GET | /konto | Firma, Rechte des Schlüssels, Bestand |
| GET | /personen | Beschäftigte auflisten |
| POST | /personen | Person anlegen |
| GET | /personen/{id} | eine Person |
| PATCH | /personen/{id} | Stammdaten ändern |
| GET | /objekte | Liegenschaften auflisten |
| POST | /objekte | anlegen oder über die Referenz aktualisieren |
| GET | /objekte/{id} | ein Objekt |
| PATCH | /objekte/{id} | ändern |
| DELETE | /objekte/{id} | stilllegen |
| GET | /zeiten | ausgewertete Arbeitszeit je Person und Tag |
| GET | /zeiten/buchungen | rohe Stempel |
| POST | /zeiten/buchungen | Stempel nachtragen |
| GET | /abwesenheiten | Urlaub, Krankheit und weitere Arten |
| POST | /abwesenheiten | Abwesenheit eintragen |
Konto und Verbindung prüfen
GET /konto liefert Firma, Rechte des Schlüssels und den Bestand. Der Aufruf, mit dem eine Anbindung anfängt.
curl -H "Authorization: Bearer cc_live_…" \
https://app.casaclock.de/api/v1/konto{
"firma": { "name": "AGS Gruppe", "kurzkennung": "ags" },
"schluessel": { "name": "Lexware-Export", "rolle": "ADMIN", "schreibend": false },
"bestand": { "personen": 28, "objekte": 317 }
}Personen
GET /personen filtert über aktiv, suche (Teil von Name oder E-Mail), limit und offset.
{
"gesamt": 28,
"personen": [
{
"id": "clx…",
"personnelNumber": "104",
"name": "Lisa Braun",
"email": "lbraun@example.de",
"role": "EMPLOYEE",
"company": "AGS Hausverwaltung",
"entryDate": "2021-04-01T00:00:00.000Z",
"exitDate": null,
"active": true,
"bundesland": "BW",
"vacationDaysPerYear": 30,
"hourlyRate": null,
"supervisorId": "clx…",
"isApprentice": false
}
]
}POST /personen verlangt name und email. Optional sind rolle, firma, eintritt, bundesland, urlaubstage, stundensatz und vorgesetzterId.
curl -X POST https://app.casaclock.de/api/v1/personen \
-H "Authorization: Bearer cc_live_…" \
-H "Content-Type: application/json" \
-d '{"name":"Lisa Braun","email":"lbraun@example.de","eintritt":"2026-10-01"}'Das Konto entsteht mit einem Zufallspasswort und Pflichtwechsel bei der ersten Anmeldung. Das Passwort wird nicht zurückgegeben, es nützt über die API niemandem: Der Zugang geht über "Zugangsdaten per Mail schicken" in der Benutzerverwaltung raus.
Die Lizenzgrenze der Firma gilt hier wie in der Oberfläche, sonst wäre die API der Weg, sie zu umgehen (409 licence_limit).
{id} ist Kennung oder E-Mail-Adresse. PATCH ändert nur die mitgeschickten Felder: name, firma, eintritt, austritt, bundesland, urlaubstage, stundensatz, vorgesetzterId, aktiv, rolle. Die E-Mail-Adresse ist gesperrt (400 immutable): Sie ist die Anmeldung, und eine Änderung hier würde jemanden aussperren, ohne dass er es merkt.
Objekte
POST /objekte verlangt name und adresse. Optional sind referenz, lat, lng, radiusM (Standard 150) und kundennummer.
curl -X POST https://app.casaclock.de/api/v1/objekte \
-H "Authorization: Bearer cc_live_…" \
-H "Content-Type: application/json" \
-d '{"name":"WEG Hauptstr. 12","adresse":"Hauptstraße 12, 70173 Stuttgart","referenz":"WEG-1042"}'Ist die referenz schon vergeben, wird das vorhandene Objekt aktualisiert statt ein Doppel angelegt; die Antwort sagt es über angelegt. Damit lässt sich der Bestand eines Verwaltungsprogramms wiederholt einspielen, ohne vorher abzugleichen.
Ohne lat und lng werden die Koordinaten aus der Anschrift ermittelt. Ohne Koordinaten erkennt die Stempeluhr das Objekt nicht. Bei neuer Anschrift ohne mitgeschickte Koordinaten wird der Geofence neu bestimmt.
DELETE legt still statt zu löschen: An einem Objekt hängen Zeitbuchungen, und ein Arbeitszeitnachweis ohne Objekt wäre lückenhaft.
Zeiten
GET /zeiten ist der Endpunkt für die Lohnbuchhaltung. Geliefert wird nicht die rohe Stempelfolge, sondern das Ergebnis der Auswertung. Pflicht sind von und bis (höchstens 400 Tage), optional person, nurMitZeit und job.
curl -H "Authorization: Bearer cc_live_…" \
"https://app.casaclock.de/api/v1/zeiten?von=2026-08-01&bis=2026-08-31&nurMitZeit=true"{
"personId": "clx…",
"personalnummer": "104",
"name": "Lisa Braun",
"datum": "2026-08-03",
"soll": 480,
"gestempelt": 507,
"anrechenbar": 477,
"gutgeschrieben": 480,
"pauseGestempelt": 0,
"pauseGesetzlich": 30,
"beginn": "07:58",
"ende": "16:25",
"abwesenheit": null,
"feiertag": null,
"abgeschlossen": true,
"offeneSchicht": false
}| Feld | Bedeutung |
|---|---|
| soll | Tagessoll aus dem Arbeitsplan, Feiertage und Abwesenheiten bereits berücksichtigt |
| gestempelt | Anwesenheit minus gestempelte Pausen |
| anrechenbar | nach Abzug der gesetzlichen Pflichtpause und mit Gutschriften für entschuldigte Abwesenheit |
| gutgeschrieben | was tatsächlich auf das Stundenkonto geht, also nur freigegebene Mehrzeit |
| pauseGesetzlich | nach Arbeitszeitgesetz nachgezogen, nicht gestempelt |
| offeneSchicht | eingestempelt, nie ausgestempelt, der Tag ist unvollständig |
Der Zeitraum ist Pflicht, weil eine offene Abfrage über alle Jahre bei größeren Firmen minutenlang läuft und niemandem hilft.
Mit job=MINI kommen stattdessen die Minijob-Tage: beginn, ende, pause, gestempelt und verdienst. Ohne Soll und ohne Stundenkonto, denn ein Minijob hat beides nicht. Der Verdienst bleibt leer, solange in den Stammdaten kein Minijob-Stundensatz steht.
Rohe Buchungen
GET /zeiten/buchungen liefert die unbewertete Stempelfolge für eigene Auswertungen. Pflicht sind von und bis, optional person, job (MAIN oder MINI), limit (Standard 200, höchstens 1000) und offset.
POST /zeiten/buchungen verlangt person, art (CLOCK_IN, CLOCK_OUT, BREAK_START, BREAK_END) und zeitpunkt. Optional sind job, objektReferenz oder objektId, adresse, lat und lng.
curl -X POST https://app.casaclock.de/api/v1/zeiten/buchungen \
-H "Authorization: Bearer cc_live_…" \
-H "Content-Type: application/json" \
-d '{"person":"lbraun@example.de","art":"CLOCK_IN","zeitpunkt":"2026-09-01T07:58:00+02:00","objektReferenz":"WEG-1042"}'Ein zweiter Stempel derselben Person, Art und Minute wird nicht angelegt (angelegt: false), damit eine wiederholte Einspielung aus einem Terminal keine Doppel erzeugt. Stempel in der Zukunft werden abgelehnt.
Abwesenheiten
GET /abwesenheiten zeigt standardmäßig nur genehmigte; status=alle zeigt auch Anträge und Ablehnungen. Weitere Filter sind von, bis, person und art. Der Zeitraum trifft jede Abwesenheit, die ihn berührt: Ein Urlaub über den Monatswechsel erscheint in beiden Monaten.
POST /abwesenheiten verlangt person, art (Schlüssel aus den Abwesenheitsarten der Firma), von und bis. Optional sind beginn und ende (HH:MM, stundenweise), entschuldigt und bemerkung.
Über die API eingetragene Abwesenheiten gelten sofort als genehmigt. Der Aufrufer ist ein Fremdsystem mit Admin-Schlüssel, kein Antragsteller; wer einen Antragsweg will, nimmt die Oberfläche.
Fehlerschlüssel
| error | HTTP | Bedeutung |
|---|---|---|
| unauthorized | 401 | Schlüssel fehlt, ist unbekannt, widerrufen oder abgelaufen |
| read_only | 403 | Schlüssel darf nur lesen |
| forbidden | 403 | schreibender Aufruf ohne Rolle ADMIN |
| tenant_inactive | 403 | Zugang der Firma gesperrt |
| tenant_expired | 403 | Laufzeit abgelaufen |
| bad_json | 400 | Rumpf ist kein JSON-Objekt |
| missing_field | 400 | Pflichtfeld fehlt |
| bad_date | 400 | Datum nicht im Format JJJJ-MM-TT |
| bad_range | 400 | bis liegt vor von |
| range_too_long | 400 | Zeitraum über 400 Tage |
| too_long | 400 | Feld überschreitet die Höchstlänge |
| bad_role, bad_type, bad_email, bad_timestamp, future | 400 | Wert unbrauchbar |
| not_found | 404 | Kennung zeigt auf nichts |
| property_not_found | 404 | Objekt zu dieser Referenz gibt es nicht |
| email_taken | 409 | Adresse bereits vergeben |
| licence_limit | 409 | Lizenzgrenze der Firma erreicht |
| immutable | 400 | Feld lässt sich über die API nicht ändern |
| internal | 500 | unerwarteter Fehler, Ursache steht im Serverprotokoll |
Beispiele
Monatsstunden nach Excel, mit Python
import csv, requests
BASE = "https://app.casaclock.de/api/v1"
KOPF = {"Authorization": "Bearer cc_live_…"}
antwort = requests.get(
f"{BASE}/zeiten",
headers=KOPF,
params={"von": "2026-08-01", "bis": "2026-08-31", "nurMitZeit": "true"},
timeout=60,
)
antwort.raise_for_status()
zeilen = antwort.json()["zeiten"]
with open("august.csv", "w", newline="", encoding="utf-8-sig") as f:
schreiber = csv.writer(f, delimiter=";")
schreiber.writerow(["Personalnummer", "Name", "Datum", "Soll", "Gutgeschrieben"])
for z in zeilen:
schreiber.writerow([
z["personalnummer"] or "",
z["name"],
z["datum"],
z["soll"],
z["gutgeschrieben"],
])Objektbestand abgleichen, mit Node
const BASE = "https://app.casaclock.de/api/v1";
const KOPF = {
Authorization: `Bearer ${process.env.CASACLOCK_KEY}`,
"Content-Type": "application/json",
};
// Der Aufruf ist über die Referenz idempotent: derselbe Lauf zweimal
// hintereinander erzeugt keine Doppel.
for (const objekt of bestandAusVerwaltungsprogramm) {
const antwort = await fetch(`${BASE}/objekte`, {
method: "POST",
headers: KOPF,
body: JSON.stringify({
name: objekt.bezeichnung,
adresse: objekt.anschrift,
referenz: objekt.nummer,
}),
});
if (!antwort.ok) {
const fehler = await antwort.json();
console.error(objekt.nummer, fehler.error, fehler.message);
}
}Terminal, das Stempel nachreicht
curl -sS -X POST https://app.casaclock.de/api/v1/zeiten/buchungen \
-H "Authorization: Bearer $CASACLOCK_KEY" \
-H "Content-Type: application/json" \
-d "{\"person\":\"$MAIL\",\"art\":\"CLOCK_IN\",\"zeitpunkt\":\"$(date -Iseconds)\"}"Bricht die Verbindung ab, kann derselbe Aufruf wiederholt werden: Die Buchung ist über Person, Art und Minute eindeutig, ein Doppel entsteht nicht.
Grenzen und Betrieb
- Die Anmeldung ist nach mehreren Fehlversuchen gebremst. Ein Fremdsystem, das einen ungültigen Schlüssel in einer Schleife probiert, läuft in diese Bremse.
- Zeiträume bei
/zeitensind auf 400 Tage begrenzt, Seiten auf 500 Einträge, bei den rohen Buchungen auf 1000. - Die API kennt keine Sitzungen und keine Cookies. Jeder Aufruf trägt den Schlüssel.
- Änderungen werden über die Versionskennung im Pfad geführt. Innerhalb von
v1kommen Felder nur hinzu, sie verschwinden nicht.