MENU navbar-image

Introduction

REST API for the Flexxr staffing platform. Manage employees, organizations, jobs, shifts, and more.

## Welcome to the Flexxr API

This documentation provides everything you need to integrate with the Flexxr staffing platform.

### Base URL
All API endpoints are prefixed with `/api/v1`.

### Authentication
Most endpoints require a **Bearer token** obtained via the login endpoint for your guard:
- **Mobile users (employees)**: `POST /api/v1/mobile/auth/login`
- **Organizations (companies)**: `POST /api/v1/org/auth/login`
- **Admins**: `POST /api/v1/admin/auth/login`

Include the token in the `Authorization` header: `Bearer {YOUR_TOKEN}`

### Organization Status Requirements
Organization accounts have status states:
- `pending_verification` — Awaiting admin KYC review
- `active` — Verified and can access all features
- `suspended` — Temporarily blocked
- `rejected` — Application rejected

Pending organizations can only access profile, notifications, and security endpoints. All other endpoints (dashboard, team, jobs, shifts) require `active` status.

### Registration Flow (Mobile)
Registration is a multi-step process:
1. `POST /api/v1/mobile/auth/register` — Submit phone/email → verification code sent
2. `POST /api/v1/mobile/auth/verify` — Verify code
3. `POST /api/v1/mobile/auth/complete` — Complete profile

### Rate Limiting
- Authentication endpoints: **5 requests/minute**
- Authenticated endpoints: **60 requests/minute**
- File uploads: **10 requests/minute**

Check `X-RateLimit-Remaining` and `X-RateLimit-Limit` response headers.

### Response Format
All responses follow a consistent JSON envelope:
```json
{
  "status": "SUCCESS|ERROR|CREATED",
  "message": "Human-readable message",
  "data": { ... },
  "errors": null,
  "meta": { "request_id": "uuid", "timestamp": "ISO 8601" }
}
```

<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Obtain a token via the login endpoint for your guard (mobile, org, or admin). Include it as Bearer {token} in the Authorization header.

Public API

Publicly accessible endpoints that do not require authentication. Use these for browsing jobs, organizations, and app settings.

App Settings

Health check endpoint for infrastructure monitoring.

Returns system health status including database, cache, and storage connectivity.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/health" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/health"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/health';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/health');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "healthy",
    "timestamp": "2026-08-05T16:18:33+00:00",
    "checks": {
        "database": {
            "status": "ok",
            "latency_ms": 3.4
        },
        "cache": {
            "status": "ok",
            "latency_ms": 0.31
        },
        "storage": {
            "status": "ok",
            "latency_ms": 0.06
        }
    }
}
 

Example response (503, Degraded):


{
    "status": "degraded",
    "timestamp": "2026-06-11T10:00:00.000000Z",
    "checks": {
        "database": {
            "status": "error",
            "latency_ms": null,
            "error": "Connection refused"
        },
        "cache": {
            "status": "ok",
            "latency_ms": 1.2
        },
        "storage": {
            "status": "ok",
            "latency_ms": 3.1
        }
    }
}
 

Request      

GET api/v1/health

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

List Public Jobs

Browse available jobs without authentication. Returns limited information to encourage registration. Only shows active jobs with future deadlines.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/public/jobs?category=gastronomy&city=Wien&lat=48.2082&lng=16.3738&radius=25&start_date=2026-06-15&end_date=2026-07-15&per_page=10" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/jobs"
);

const params = {
    "category": "gastronomy",
    "city": "Wien",
    "lat": "48.2082",
    "lng": "16.3738",
    "radius": "25",
    "start_date": "2026-06-15",
    "end_date": "2026-07-15",
    "per_page": "10",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/jobs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'category' => 'gastronomy',
            'city' => 'Wien',
            'lat' => '48.2082',
            'lng' => '16.3738',
            'radius' => '25',
            'start_date' => '2026-06-15',
            'end_date' => '2026-07-15',
            'per_page' => '10',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/jobs')
      .replace(queryParameters: {
        'category': 'gastronomy',
        'city': 'Wien',
        'lat': '48.2082',
        'lng': '16.3738',
        'radius': '25',
        'start_date': '2026-06-15',
        'end_date': '2026-07-15',
        'per_page': '10',
      });

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "jobs": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 10,
            "total": 0
        }
    },
    "message": "Register or log in to apply for this job."
}
 

Request      

GET api/v1/public/jobs

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

category   string  optional    

Filter by job category. Example: gastronomy

city   string  optional    

Filter by city. Example: Wien

lat   number  optional    

Latitude for radius search. Example: 48.2082

lng   number  optional    

Longitude for radius search. Example: 16.3738

radius   integer  optional    

Radius in km (requires lat/lng). Example: 25

start_date   string  optional    

date Filter jobs starting on or after this date. Example: 2026-06-15

end_date   string  optional    

date Filter jobs ending on or before this date. Example: 2026-07-15

per_page   integer  optional    

Items per page (max 20). Example: 10

Get Public Job Details

View details of a specific job without authentication. Returns limited information (no exact wage, no contact details) to encourage registration.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/public/jobs/550e8400-e29b-41d4-a716-446655440000" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/jobs/550e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/jobs/550e8400-e29b-41d4-a716-446655440000';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/jobs/550e8400-e29b-41d4-a716-446655440000');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "title": "Accountant",
            "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
            "category": {
                "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                "slug": "office",
                "name": "Büro",
                "name_localized": "Office",
                "icon": "briefcase",
                "color": null
            },
            "location_city": "East Eliseo",
            "start_date": "2026-08-12",
            "end_date": "2026-08-19",
            "shift_types": null,
            "shift_start_time": "01:49:00",
            "shift_end_time": "18:57:00",
            "workers_needed": null,
            "wage_hint": null,
            "requirements": {
                "qualifications": [],
                "languages": null
            },
            "organization": {
                "name": "Caritas",
                "logo_url": null,
                "city": "Vienna"
            },
            "application_deadline": "2026-09-05 16:18"
        }
    },
    "message": "Register or log in to apply for this job."
}
 

Example response (404, Not Found):


{
    "status": "RESOURCE_NOT_FOUND",
    "message": "Job not found."
}
 

Request      

GET api/v1/public/jobs/{jobId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

The job UUID. Example: 550e8400-e29b-41d4-a716-446655440000

List all active legal documents (public endpoint, no auth required).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/public/legal" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/legal"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/legal';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/legal');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Legal documents retrieved.",
    "data": {
        "documents": [
            {
                "id": "019fd2b7-ef7c-732a-b29e-ef789f899c9d",
                "type": "aug_info",
                "title": "Information gemäß AÜG (Arbeitskräfteüberlassungsgesetz)",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": false
            },
            {
                "id": "019fd2b7-ef86-70be-8580-5a1ac1757a9a",
                "type": "cookie_policy",
                "title": "Cookie-Richtlinie",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": false
            },
            {
                "id": "019fd2b7-ef76-73b5-a9c5-d7cca778324b",
                "type": "data_processing",
                "title": "Einwilligung zur Datenverarbeitung",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": true
            },
            {
                "id": "019fd2b7-ef8a-7286-ada6-84f13beb905f",
                "type": "imprint",
                "title": "Impressum",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": false
            },
            {
                "id": "019fd2b7-ef68-7312-a1a3-6b14820cf9e2",
                "type": "privacy_policy",
                "title": "Datenschutzerklärung",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": true
            },
            {
                "id": "019fd2b7-ef83-7298-a78c-af4b1e36b67b",
                "type": "svnr_consent",
                "title": "Einwilligung zur Verarbeitung der Sozialversicherungsnummer",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": true
            },
            {
                "id": "019fd2b7-ef6f-72ae-8c86-d9977da26ec0",
                "type": "terms_of_service",
                "title": "Allgemeine Geschäftsbedingungen (AGB)",
                "version": 1,
                "locale": "de",
                "published_at": "2026-08-05T16:18:17+00:00",
                "requires_reacceptance": true
            }
        ],
        "available_locales": [
            "de",
            "en"
        ]
    }
}
 

Get the latest active version of a legal document by type (public endpoint).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/public/legal/latest/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/legal/latest/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/legal/latest/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/legal/latest/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "51e25b3b-a569-46f5-b6b5-5047b558e6d2",
        "timestamp": "2026-08-21T05:14:32.469716Z"
    }
}
 

Show a specific legal document by ID (public endpoint, no auth required).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/public/legal/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/legal/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/legal/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/legal/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Legal document retrieved.",
    "data": {
        "id": "019fd2b7-ef68-7312-a1a3-6b14820cf9e2",
        "type": "privacy_policy",
        "title": "Datenschutzerklärung",
        "content": "# Datenschutzerklärung\n\n**Stand: 05.08.2026**\n**Version: 1.0**\n\n---\n\n## 1. Verantwortlicher\n\n**Flexxer**\nMusterstraße 1\n1010 Wien, Österreich\n\n- **Firmenbuchnummer:** FN 123456x\n- **UID-Nummer:** ATU12345678\n- **Telefon:** +43 1 234 5678\n- **E-Mail:** privacy@flexxr.at\n- **Website:** https://flexxr.at\n\n### 1.1 Datenschutzbeauftragter\n\nFür Fragen zum Datenschutz erreichen Sie unseren Datenschutzbeauftragten unter:\n- **E-Mail:** dpo@flexxr.at\n\n---\n\n## 2. Rechtsgrundlagen der Verarbeitung\n\nWir verarbeiten Ihre personenbezogenen Daten ausschließlich auf Basis der folgenden Rechtsgrundlagen gemäß **Datenschutz-Grundverordnung (DSGVO)** und dem **österreichischen Datenschutzgesetz (DSG)**:\n\n| Rechtsgrundlage | DSGVO Artikel | Anwendungsbereich |\n|-----------------|---------------|-------------------|\n| Vertragserfüllung | Art. 6 Abs. 1 lit. b | Durchführung des Arbeits-/Überlassungsvertrags |\n| Rechtliche Verpflichtung | Art. 6 Abs. 1 lit. c | ASVG-Meldungen, Lohnsteuer, AÜG-Compliance |\n| Berechtigtes Interesse | Art. 6 Abs. 1 lit. f | Betrugsprävention, IT-Sicherheit |\n| Einwilligung | Art. 6 Abs. 1 lit. a | Marketing, optionale Datenverarbeitung |\n\n---\n\n## 3. Kategorien personenbezogener Daten\n\n### 3.1 Stammdaten\n- Vor- und Nachname\n- Geburtsdatum und Geburtsort\n- Staatsangehörigkeit\n- Geschlecht\n- Familienstand\n\n### 3.2 Kontaktdaten\n- E-Mail-Adresse\n- Telefonnummer\n- Wohnadresse\n\n### 3.3 Beschäftigungsdaten\n- Sozialversicherungsnummer (SVNR) gemäß ASVG\n- Steuernummer\n- Bankverbindung (IBAN)\n- Qualifikationen und Zertifikate\n- Arbeitszeitaufzeichnungen\n\n### 3.4 Besondere Kategorien (Art. 9 DSGVO)\n- Gesundheitsdaten (nur bei medizinischen Bescheinigungen)\n- Gewerkschaftszugehörigkeit (nur bei KV-Anwendung)\n\nDiese Daten werden nur mit ausdrücklicher Einwilligung oder aufgrund gesetzlicher Verpflichtung verarbeitet.\n\n---\n\n## 4. Zwecke der Verarbeitung\n\n### 4.1 Vertragserfüllung\n- Durchführung des Arbeitsvertrags gemäß **AÜG § 11**\n- Vermittlung von Arbeitseinsätzen\n- Arbeitszeiterfassung und Abrechnung\n- Lohn- und Gehaltsabrechnung\n\n### 4.2 Gesetzliche Verpflichtungen\n- ELDA-Meldungen an die ÖGK (gemäß **ASVG §§ 33-34**)\n- Lohnsteuerabzug und Meldung an das Finanzamt (**EStG**)\n- Aufbewahrungspflichten (**BAO § 132**: 7 Jahre für Geschäftsunterlagen)\n- AÜG-Meldepflichten an die Gewerbebehörde\n\n### 4.3 Berechtigte Interessen\n- Betrugsprävention und Identitätsprüfung\n- IT-Sicherheit und Systemstabilität\n- Qualitätssicherung und Prozessoptimierung\n\n---\n\n## 5. Empfänger der Daten\n\nIhre Daten werden an folgende Kategorien von Empfängern übermittelt:\n\n| Empfänger | Zweck | Rechtsgrundlage |\n|-----------|-------|-----------------|\n| ÖGK (Österreichische Gesundheitskasse) | Sozialversicherungsmeldungen | ASVG §§ 33-34 |\n| Finanzamt | Lohnsteuer, Lohnzettel | EStG § 84 |\n| Beschäftiger (Kundenunternehmen) | Arbeitseinsatz | AÜG § 12 |\n| Lohnverrechnungsdienstleister | Gehaltsabrechnung | Auftragsverarbeitung |\n| IT-Dienstleister | Hosting, Cloud-Services | Auftragsverarbeitung |\n\n---\n\n## 6. Speicherdauer\n\n| Datenkategorie | Speicherdauer | Rechtsgrundlage |\n|----------------|---------------|-----------------|\n| Lohnabrechnungsunterlagen | 7 Jahre | BAO § 132 |\n| Arbeitszeitaufzeichnungen | 3 Jahre nach Ende des Arbeitsverhältnisses | AZG § 26 |\n| SV-Meldungen | 7 Jahre | ASVG |\n| Bewerbungsunterlagen (bei Ablehnung) | 6 Monate | DSGVO Art. 17 |\n| Vertragsdokumente | 30 Jahre (Verjährungsfrist) | ABGB § 1489 |\n\n---\n\n## 7. Ihre Rechte nach DSGVO\n\nSie haben folgende Rechte bezüglich Ihrer personenbezogenen Daten:\n\n### 7.1 Auskunftsrecht (Art. 15 DSGVO)\nSie haben das Recht, eine Bestätigung darüber zu verlangen, ob personenbezogene Daten verarbeitet werden, und gegebenenfalls Auskunft über diese Daten zu erhalten.\n\n### 7.2 Recht auf Berichtigung (Art. 16 DSGVO)\nSie haben das Recht, unrichtige personenbezogene Daten unverzüglich berichtigen zu lassen.\n\n### 7.3 Recht auf Löschung (Art. 17 DSGVO)\nSie haben das Recht, die Löschung Ihrer Daten zu verlangen, sofern keine gesetzlichen Aufbewahrungspflichten entgegenstehen.\n\n### 7.4 Recht auf Einschränkung (Art. 18 DSGVO)\nSie haben das Recht, die Einschränkung der Verarbeitung zu verlangen.\n\n### 7.5 Recht auf Datenübertragbarkeit (Art. 20 DSGVO)\nSie haben das Recht, Ihre Daten in einem strukturierten, gängigen und maschinenlesbaren Format zu erhalten.\n\n### 7.6 Widerspruchsrecht (Art. 21 DSGVO)\nSie haben das Recht, gegen die Verarbeitung Ihrer Daten Widerspruch einzulegen.\n\n### 7.7 Recht auf Widerruf der Einwilligung (Art. 7 Abs. 3 DSGVO)\nSie haben das Recht, erteilte Einwilligungen jederzeit zu widerrufen.\n\n---\n\n## 8. Beschwerderecht bei der Aufsichtsbehörde\n\nSie haben das Recht, eine Beschwerde bei der zuständigen Aufsichtsbehörde einzureichen:\n\n**Österreichische Datenschutzbehörde**\nBarichgasse 40-42\n1030 Wien\n\n- **E-Mail:** dsb@dsb.gv.at\n- **Website:** https://www.dsb.gv.at\n\n---\n\n## 9. Automatisierte Entscheidungsfindung\n\nEs findet keine automatisierte Entscheidungsfindung im Sinne von **Art. 22 DSGVO** statt, die rechtliche Wirkung entfaltet oder Sie in ähnlicher Weise erheblich beeinträchtigt.\n\n---\n\n## 10. Drittlandübermittlung\n\nEine Übermittlung Ihrer Daten in Drittländer (außerhalb des EWR) erfolgt nur:\n- Auf Basis von Angemessenheitsbeschlüssen der EU-Kommission\n- Unter Verwendung von EU-Standardvertragsklauseln\n- Mit Ihrer ausdrücklichen Einwilligung\n\n---\n\n## 11. Änderungen dieser Datenschutzerklärung\n\nWir behalten uns vor, diese Datenschutzerklärung anzupassen, um sie an geänderte Rechtslagen oder bei Änderungen unserer Dienste anzupassen. Die aktuelle Version finden Sie stets unter: https://flexxr.at/legal/privacy_policy\n\n---\n\n## 12. Kontakt\n\nBei Fragen zum Datenschutz wenden Sie sich bitte an:\n\n**Flexxer**\nMusterstraße 1, 1010 Wien\nE-Mail: privacy@flexxr.at\n",
        "content_format": "markdown",
        "version": 1,
        "locale": "de",
        "published_at": "2026-08-05T16:18:17+00:00",
        "effective_until": null,
        "requires_reacceptance": true,
        "content_hash": "7d28b9a8ddc190e477a58a1d95a0a50d2f0428e19594df2dfff7742c94e784ff"
    }
}
 

Get Privacy Contact

Controller identity and data-protection contact points (public endpoint).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/public/privacy-contact" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/privacy-contact"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/privacy-contact';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/privacy-contact');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "801b4111-2078-4137-a396-974adbf7aed6",
        "timestamp": "2026-08-21T05:14:32.487770Z"
    }
}
 

Request      

GET api/v1/public/privacy-contact

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Dashboard

Get Public Dashboard (Guest Home Screen)

Returns the public home screen data for unauthenticated users:

Does NOT include:

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/home" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/home"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/home';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/home');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "popular_jobs": [
            {
                "id": "019fd2b8-22cf-71c5-8edd-5e3ce9d29e8b",
                "title": "Promoter*in Vienna Marathon",
                "description": "Für ein großes Outdoor-Event suchen wir Promoter*innen und Koordinator*innen. Teamgeist und Flexibilität sind gefragt.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                    "slug": "event",
                    "name": "Events",
                    "name_localized": "Events",
                    "icon": "calendar-star",
                    "color": null
                },
                "tags": [
                    "Events",
                    "Tagschicht",
                    "Veranstaltungserfahrung",
                    "Teamfähigkeit"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Promoter*in Vienna Marathon",
                    "address": "Hauptstraße 48, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3738,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-26",
                    "end_date": "2026-08-28",
                    "shift_start_time": "09:00:00",
                    "shift_end_time": "17:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 13.5,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 20,
                    "filled": 0,
                    "available": 20
                },
                "deadlines": {
                    "application": "2026-08-23T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:30+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null,
                "wage_hint": "€12-14/hr"
            },
            {
                "id": "019fd2b8-2599-7355-a7f1-dcb185b05faa",
                "title": "Rezeptionist*in Hotel Sacher",
                "description": "Für unser Haus suchen wir freundliche und belastbare Mitarbeiter*innen für den Front- und Back-Office-Bereich.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                    "slug": "hotel",
                    "name": "Hotellerie",
                    "name_localized": "Hospitality",
                    "icon": "hotel",
                    "color": null
                },
                "tags": [
                    "Hospitality",
                    "Tagschicht",
                    "Hotellerie-Erfahrung",
                    "Deutsch B2"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Rezeptionist*in Hotel Sacher",
                    "address": "Hauptstraße 87, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2034,
                    "lng": 16.3694,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-17",
                    "end_date": "2026-08-19",
                    "shift_start_time": "09:00:00",
                    "shift_end_time": "17:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 17.5,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 4,
                    "filled": 0,
                    "available": 4
                },
                "deadlines": {
                    "application": "2026-08-14T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:31+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null,
                "wage_hint": "€14-18/hr"
            },
            {
                "id": "019fd2b8-1f86-7376-9a3a-c072ab5675cb",
                "title": "Service-Mitarbeiter*in Wiener Prater Festival",
                "description": "Wir suchen engagierte Service-Mitarbeiter*innen für unser Team. Du bringst Freude am Umgang mit Gästen mit und arbeitest gerne im Team.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Tagschicht",
                    "Serviceerfahrung",
                    "Deutsch B2"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Service-Mitarbeiter*in Wiener Prater Festival",
                    "address": "Hauptstraße 91, 1020 Wien",
                    "city": "Wien",
                    "postal_code": "1020",
                    "lat": 48.2117,
                    "lng": 16.3969,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-19",
                    "end_date": "2026-08-21",
                    "shift_start_time": "09:00:00",
                    "shift_end_time": "17:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 15.5,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 12,
                    "filled": 0,
                    "available": 12
                },
                "deadlines": {
                    "application": "2026-08-16T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null,
                "wage_hint": "€14-18/hr"
            },
            {
                "id": "019fd2b8-21c8-72cc-80b1-47fd13450522",
                "title": "Barkeeper*in Sommernacht Open Air",
                "description": "Wir suchen engagierte Service-Mitarbeiter*innen für unser Team. Du bringst Freude am Umgang mit Gästen mit und arbeitest gerne im Team.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Nachtschicht",
                    "Serviceerfahrung",
                    "Deutsch B2"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Barkeeper*in Sommernacht Open Air",
                    "address": "Hauptstraße 35, 1060 Wien",
                    "city": "Wien",
                    "postal_code": "1060",
                    "lat": 48.1953,
                    "lng": 16.356,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-15",
                    "end_date": "2026-08-17",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 8,
                    "filled": 0,
                    "available": 8
                },
                "deadlines": {
                    "application": "2026-08-12T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:30+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null,
                "wage_hint": "€14-18/hr"
            },
            {
                "id": "019fd2b8-24b0-700a-ae2a-142f5e48aead",
                "title": "Lagerhelfer*in Logistikzentrum Inzersdorf",
                "description": "Für unser Logistikzentrum suchen wir zuverlässige Helfer*innen. Körperliche Belastbarkeit und Pünktlichkeit sind Voraussetzung.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f38b-7209-8b83-b345b172def2",
                    "slug": "warehouse",
                    "name": "Lager",
                    "name_localized": "Warehouse",
                    "icon": "industry",
                    "color": null
                },
                "tags": [
                    "Warehouse",
                    "Nachtschicht",
                    "Staplerführerschein von Vorteil",
                    "Körperliche Belastbarkeit"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Lagerhelfer*in Logistikzentrum Inzersdorf",
                    "address": "Hauptstraße 28, 1230 Wien",
                    "city": "Wien",
                    "postal_code": "1230",
                    "lat": 48.1454,
                    "lng": 16.3499,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-08-14",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 14.8,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 15,
                    "filled": 0,
                    "available": 15
                },
                "deadlines": {
                    "application": "2026-08-09T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:31+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null,
                "wage_hint": "€14-18/hr"
            }
        ],
        "recent_jobs": [
            {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant",
                "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                    "slug": "office",
                    "name": "Büro",
                    "name_localized": "Office",
                    "icon": "briefcase",
                    "color": null
                },
                "tags": [
                    "Office",
                    "Nachtschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Barrows, Christiansen and Jones",
                    "address": "586 Tremaine Row",
                    "city": "East Eliseo",
                    "postal_code": "10547-3697",
                    "lat": 46.931383,
                    "lng": 11.998284,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-08-19",
                    "shift_start_time": "01:49:00",
                    "shift_end_time": "18:57:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 17.133333333333333
                },
                "compensation": {
                    "hourly_rate_gross": 32.08,
                    "supplements": {
                        "night": 0.7,
                        "weekend": 3.86
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 5,
                    "filled": 0,
                    "available": 5
                },
                "deadlines": {
                    "application": "2026-09-05T16:18:32+00:00"
                },
                "created_at": "2026-08-05T16:18:32+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null,
                "wage_hint": "€30+/hr"
            },
            {
                "id": "019fd2b8-1405-709f-bca9-b0a418fb6a75",
                "title": "Silvesternacht Service",
                "description": "Wir suchen motivierte Silvesternacht Service für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Nachtschicht",
                    "HACCP Kenntnisse von Vorteil"
                ],
                "organization": {
                    "id": "019fd2b7-fe56-70d1-bb3a-641339ce636a",
                    "name": "Lagerhaus Steiermark",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Hotel Imperial Wien",
                    "address": "Kärntner Ring 16, 1015 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-02-09",
                    "end_date": "2027-02-10",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 25,
                    "supplements": {
                        "night_bonus": 50,
                        "holiday_bonus": 100,
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 12,
                    "filled": 0,
                    "available": 12
                },
                "deadlines": {
                    "application": "2027-02-04T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:26+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null,
                "wage_hint": "€22-26/hr"
            },
            {
                "id": "019fd2b8-15b5-7019-8521-6ea79edc4ba5",
                "title": "Garderobenservice Ball",
                "description": "Für unsere bevorstehende Veranstaltung suchen wir tatkräftige Unterstützung. Als Garderobenservice Ball sind Sie das Aushängeschild unserer Veranstaltung und sorgen für einen reibungslosen Ablauf.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                    "slug": "event",
                    "name": "Events",
                    "name_localized": "Events",
                    "icon": "calendar-star",
                    "color": null
                },
                "tags": [
                    "Events",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-ff32-7117-ab28-06e2d3082002",
                    "name": "Bildungszentrum Mitte",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Wiener Rathaus",
                    "address": "Friedrich-Schmidt-Platz 1, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-01-22",
                    "end_date": "2027-01-23",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 14.5,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 10,
                    "filled": 0,
                    "available": 10
                },
                "deadlines": {
                    "application": "2027-01-19T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null,
                "wage_hint": "€14-18/hr"
            },
            {
                "id": "019fd2b8-18d3-718f-bbe2-32c97903f559",
                "title": "Verkaufshilfe Weihnachtsgeschäft",
                "description": "Für unser Retail-Team suchen wir freundliche und kundenorientierte Verkaufshilfe Weihnachtsgeschäft. Sie beraten unsere Kunden, wickeln Kassiervorgänge ab und sorgen für eine ansprechende Warenpräsentation.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f37d-712d-92b3-aa5540f6b2a7",
                    "slug": "retail",
                    "name": "Einzelhandel",
                    "name_localized": "Retail",
                    "icon": "shopping-cart",
                    "color": null
                },
                "tags": [
                    "Retail",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-fca6-71cf-8683-c97f6f50a24a",
                    "name": "Reinigungsservice Alpin",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Wiener Innenstadt",
                    "address": "Kärntner Straße 15, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-01-12",
                    "end_date": "2027-02-01",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 13.5,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 10,
                    "filled": 0,
                    "available": 10
                },
                "deadlines": {
                    "application": "2027-01-08T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:28+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null,
                "wage_hint": "€12-14/hr"
            },
            {
                "id": "019fd2b8-13e2-7390-a987-e12b987cdbb8",
                "title": "Weihnachtsmarkt Servicekraft",
                "description": "Wir suchen motivierte Weihnachtsmarkt Servicekraft für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Tagschicht",
                    "HACCP Kenntnisse von Vorteil"
                ],
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Christkindlmarkt Rathausplatz",
                    "address": "Rathausplatz 1, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-01-02",
                    "end_date": "2027-02-01",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 14,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 30,
                    "filled": 0,
                    "available": 30
                },
                "deadlines": {
                    "application": "2026-12-29T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:26+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null,
                "wage_hint": "€14-18/hr"
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/home

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Invitations

Accept Invitation

Phase-1 of the invitation signup flow.

Stamps accepted_at and accepted_by_email on the invitation row, then returns the resolved preset key and label so the front-end can render a confirmation screen before the user completes signup.

The endpoint is idempotent until member_created_at is set: a second call for the same token (e.g. page reload) returns the same 200 body instead of a conflict error.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/public/org/invitations/accept" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"token\": \"abc123...\",
    \"accepted_by_email\": \"invitee@example.com\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/org/invitations/accept"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "abc123...",
    "accepted_by_email": "invitee@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/org/invitations/accept';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'token' => 'abc123...',
            'accepted_by_email' => 'invitee@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/org/invitations/accept');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "token": "abc123...",
    "accepted_by_email": "invitee@example.com"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Invitation accepted.",
    "data": {
        "status": "accepted",
        "preset_key": "viewer",
        "preset_label": "Viewer"
    },
    "errors": null,
    "meta": {
        "request_id": "cb6e38a7-5b78-481e-aa81-9f9501c5c4d0",
        "timestamp": "2026-08-05T16:19:05.938455Z"
    }
}
 

Example response (410):


{
    "status": "INVITATION_EXPIRED",
    "message": "Invitation has expired."
}
 

Example response (422):


{
    "status": "INVITATION_REVOKED",
    "message": "Invitation has been revoked."
}
 

Request      

POST api/v1/public/org/invitations/accept

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The invitation token. Example: abc123...

accepted_by_email   string  optional    

nullable The email used to accept. Defaults to the invitation email. Example: invitee@example.com

Complete Invitation Signup

Phase-2 of the invitation signup flow.

Creates a User and OrganizationMember record, assigns the ACL preset from the invitation (with fallbacks for legacy rows), and stamps member_created_at on the invitation.

Preset resolution order:

  1. invitation.preset_key if non-null and registered in catalog → 'invitation'
  2. invitation.preset_key is null → role map (PRESET_FOR_LEGACY_ROLE) → 'fallback_legacy_role'
  3. invitation.preset_key is stale (not in catalog) → 'viewer' → 'fallback_missing_catalog'
Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/public/org/invitations/signup" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"token\": \"abc123...\",
    \"name\": \"Jane Doe\",
    \"password\": \"SecureP4ssword!1\",
    \"password_confirmation\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/public/org/invitations/signup"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "abc123...",
    "name": "Jane Doe",
    "password": "SecureP4ssword!1",
    "password_confirmation": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/public/org/invitations/signup';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'token' => 'abc123...',
            'name' => 'Jane Doe',
            'password' => 'SecureP4ssword!1',
            'password_confirmation' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/public/org/invitations/signup');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "token": "abc123...",
    "name": "Jane Doe",
    "password": "SecureP4ssword!1",
    "password_confirmation": "architecto"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Account created.",
    "data": {
        "acl": {
            "scope": "org-member",
            "presets": [
                "viewer"
            ]
        },
        "presets": [
            "viewer"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "8b8ff3cf-05df-49f1-aa0c-aaacc096a0ea",
        "timestamp": "2026-08-05T16:19:06.535113Z"
    }
}
 

Request      

POST api/v1/public/org/invitations/signup

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The invitation token. Example: abc123...

name   string     

Full name for the new member account. Example: Jane Doe

password   string     

Password (min 8 chars). Example: SecureP4ssword!1

password_confirmation   string     

Must match password. Example: architecto

Mobile API

Endpoints for the mobile application (employee users). All endpoints (except authentication) require a valid Bearer token with 2FA verification.

Authentication

Check Email Availability

Checks if an email address is available for registration.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/check-email" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"max@example.com\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/check-email"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "max@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/check-email';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'max@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/check-email');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "max@example.com"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Email is available.",
    "data": {
        "available": true
    },
    "errors": null,
    "meta": {
        "request_id": "307c14e3-4d54-46dc-bf54-13557ff1a48a",
        "timestamp": "2026-08-05T16:18:34.040638Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The email field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-11T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/check-email

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to check. Example: max@example.com

Check Phone Availability

Checks if a phone number is available for registration.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/check-phone" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/check-phone"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "country_code": "+43",
    "phone_number": "6641234567"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/check-phone';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'country_code' => '+43',
            'phone_number' => '6641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/check-phone');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "country_code": "+43",
    "phone_number": "6641234567"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Phone number is already registered.",
    "data": {
        "available": false
    },
    "errors": null,
    "meta": {
        "request_id": "c64348be-986c-4365-8fa5-702eeb4a5699",
        "timestamp": "2026-08-05T16:18:34.044831Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "phone_number",
            "message": "The phone number field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-11T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/check-phone

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

country_code   string     

Country code with + prefix. Example: +43

phone_number   string     

Phone number without country code. Example: 6641234567

Confirm Email Verification

Consumes the token from the email verification link. On success the user account is verified and the EmployeeProfile advances to profile_incomplete. An invalid, expired, or already-used token returns a 410 Gone response so the client can prompt the user to request a new link.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/confirm" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"token\": \"abc123def456...\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/confirm"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "token": "abc123def456..."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'token' => 'abc123def456...',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/confirm');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "token": "abc123def456..."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Email verified successfully.",
    "data": {
        "email": "boris@example.com",
        "status": "profile_incomplete"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (410, Token Invalid or Expired):


{
    "status": "VERIFICATION_LINK_EXPIRED",
    "message": "Verification link is invalid, expired, or already used. Please request a new one.",
    "data": null,
    "errors": [
        {
            "field": "token",
            "message": "Verification link is invalid, expired, or already used."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "token",
            "message": "The token field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/verify-email/confirm

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address from the verification link. Example: boris@example.com

token   string     

Verification token from the emailed link. Example: abc123def456...

Resend Verification Link

Resends the email verification link to the given address. Silently succeeds when the email is not registered or already verified to prevent account enumeration.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/resend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-email/resend');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If this address has a pending verification, a new link has been sent.",
    "data": {
        "email": "boris@example.com"
    },
    "errors": null,
    "meta": {
        "request_id": "4d862349-74df-4e13-892f-638a1e742726",
        "timestamp": "2026-08-05T16:18:34.053350Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The email must be a valid email address."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/verify-email/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to resend the link to. Example: boris@example.com

Register User

Creates the user account in REGISTERED status and queues an email verification link. Phone (country_code + phone_number) and GDPR document IDs (terms_document_id, privacy_document_id, processing_data_document_id) are all required. No session token is issued; the user verifies via the emailed link and then signs in.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/register" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"first_name\": \"Boris\",
    \"last_name\": \"Jacobi\",
    \"email\": \"boris@example.com\",
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\",
    \"password\": \"SuperSecret123!\",
    \"password_confirmation\": \"SuperSecret123!\",
    \"terms_document_id\": \"01958e69-a8fe-7000-9abc-123456789abc\",
    \"privacy_document_id\": \"01958e69-b8fe-7000-9abc-123456789def\",
    \"processing_data_document_id\": \"01958e69-c8fe-7000-9abc-123456789ghi\",
    \"marketing_consent\": false,
    \"referral_code\": \"AB12CD34\",
    \"language\": \"de\",
    \"device_info\": {
        \"device_id\": \"b\",
        \"platform\": \"n\",
        \"os_version\": \"g\",
        \"app_version\": \"z\",
        \"device_model\": \"m\"
    }
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/register"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "first_name": "Boris",
    "last_name": "Jacobi",
    "email": "boris@example.com",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "SuperSecret123!",
    "password_confirmation": "SuperSecret123!",
    "terms_document_id": "01958e69-a8fe-7000-9abc-123456789abc",
    "privacy_document_id": "01958e69-b8fe-7000-9abc-123456789def",
    "processing_data_document_id": "01958e69-c8fe-7000-9abc-123456789ghi",
    "marketing_consent": false,
    "referral_code": "AB12CD34",
    "language": "de",
    "device_info": {
        "device_id": "b",
        "platform": "n",
        "os_version": "g",
        "app_version": "z",
        "device_model": "m"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/register';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'first_name' => 'Boris',
            'last_name' => 'Jacobi',
            'email' => 'boris@example.com',
            'country_code' => '+43',
            'phone_number' => '6641234567',
            'password' => 'SuperSecret123!',
            'password_confirmation' => 'SuperSecret123!',
            'terms_document_id' => '01958e69-a8fe-7000-9abc-123456789abc',
            'privacy_document_id' => '01958e69-b8fe-7000-9abc-123456789def',
            'processing_data_document_id' => '01958e69-c8fe-7000-9abc-123456789ghi',
            'marketing_consent' => false,
            'referral_code' => 'AB12CD34',
            'language' => 'de',
            'device_info' => ['device_id' => 'b', 'platform' => 'n', 'os_version' => 'g', 'app_version' => 'z', 'device_model' => 'm'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/register');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "first_name": "Boris",
    "last_name": "Jacobi",
    "email": "boris@example.com",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "SuperSecret123!",
    "password_confirmation": "SuperSecret123!",
    "terms_document_id": "01958e69-a8fe-7000-9abc-123456789abc",
    "privacy_document_id": "01958e69-b8fe-7000-9abc-123456789def",
    "processing_data_document_id": "01958e69-c8fe-7000-9abc-123456789ghi",
    "marketing_consent": false,
    "referral_code": "AB12CD34",
    "language": "de",
    "device_info": {
        "device_id": "b",
        "platform": "n",
        "os_version": "g",
        "app_version": "z",
        "device_model": "m"
    }
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Success):


{
    "status": "CREATED",
    "message": "Registration successful.",
    "data": {
        "employee_id": "usr-uuid",
        "email": "boris@example.com",
        "status": "registered",
        "verification_sent": true,
        "next_step": "email_verification"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, Email Already Registered):


{
    "status": "EMAIL_ALREADY_EXISTS",
    "message": "This email is already registered.",
    "data": {
        "can_login": true,
        "can_reset_password": true
    },
    "errors": [
        {
            "field": "email",
            "message": "This email is already registered."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "The password must be at least 8 characters."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests.",
    "data": {
        "retry_after": 900
    },
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

first_name   string     

First name (2-50 chars, letters/spaces/hyphens/apostrophes only). Example: Boris

last_name   string     

Last name (2-50 chars, letters/spaces/hyphens/apostrophes only). Example: Jacobi

email   string     

User's email address. Example: boris@example.com

country_code   string     

Country calling code (E.164 prefix). Example: +43

phone_number   string     

Phone number digits only. Example: 6641234567

password   string     

Password (min 8, max 128, mixed case + numbers + symbols required). Example: SuperSecret123!

password_confirmation   string     

Password confirmation. Example: SuperSecret123!

terms_document_id   string     

UUID of Terms of Service version accepted by user (GDPR Art. 7). Providing this ID is the acceptance proof. Example: 01958e69-a8fe-7000-9abc-123456789abc

privacy_document_id   string     

UUID of Privacy Policy version accepted by user (GDPR Art. 7). Providing this ID is the acceptance proof. Example: 01958e69-b8fe-7000-9abc-123456789def

processing_data_document_id   string     

UUID of Data Processing Agreement version accepted by user (GDPR Art. 7). Providing this ID is the acceptance proof. Example: 01958e69-c8fe-7000-9abc-123456789ghi

marketing_consent   boolean  optional    

optional Opt in/out of marketing emails. Example: false

referral_code   string  optional    

optional 8-character alphanumeric referral code. Example: AB12CD34

language   string  optional    

optional Preferred language (de, en, hr). Default: de. Allowed: de, en, hr. Example: de

device_info   object  optional    
device_id   string  optional    

Must not be greater than 255 characters. Example: b

platform   string  optional    

Must not be greater than 50 characters. Example: n

os_version   string  optional    

Must not be greater than 50 characters. Example: g

app_version   string  optional    

Must not be greater than 50 characters. Example: z

device_model   string  optional    

Must not be greater than 100 characters. Example: m

Login User

Authenticates an end-user against the user guard and issues a Sanctum token. If the user has 2FA enabled a limited challenge token is returned and the client must complete the two-factor challenge before accessing any protected endpoints.

Supports login via email or phone number:

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\",
    \"password\": \"SuperSecret123!\",
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\",
    \"remember_me\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "SuperSecret123!",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH",
    "remember_me": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'country_code' => '+43',
            'phone_number' => '6641234567',
            'password' => 'SuperSecret123!',
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
            'remember_me' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/login');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "SuperSecret123!",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH",
    "remember_me": false
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Login successful.",
    "data": {
        "token": "5|1f6S0lbLjAalzY2SdWUu1tdHThVkOGy2vnHx7WdX9e1f8795",
        "refresh_token": "6|IZHjJiYI6X4IOrBpe0wRa0MXGE1sdBHywaUzaQDMcf7756ed",
        "expires_in": 604800,
        "user": {
            "id": "019fd2b7-f271-73fd-8268-848be381e136",
            "first_name": "Anna",
            "last_name": "Neuling",
            "email": "pending@demo.flexxr.at",
            "country_code": "+43",
            "phone_number": "6769876543",
            "status": "active",
            "avatar_url": null,
            "bio": null,
            "date_of_birth": null,
            "country": null,
            "city": null,
            "notify_push": true,
            "notify_email": true,
            "notify_sms": true,
            "notify_marketing": false,
            "email_verified_at": "2026-08-05T16:18:32+00:00",
            "phone_verified_at": "2026-08-05T16:18:32+00:00",
            "created_at": "2026-08-05T16:18:18+00:00",
            "updated_at": "2026-08-05T16:18:34+00:00",
            "employee_profile": {
                "address_line_1": "Neugasse 5",
                "address_line_2": null,
                "postal_code": "4020"
            }
        },
        "employee_status": "active"
    },
    "errors": null,
    "meta": {
        "request_id": "db53cffc-fb25-412c-8049-67967cc3b0d8",
        "timestamp": "2026-08-05T16:18:34.317645Z"
    }
}
 

Example response (401, 2FA Required):


{
    "status": "TWO_FACTOR_REQUIRED",
    "message": "Two-factor authentication is required.",
    "data": {
        "token": "2|challenge-token...",
        "two_factor_method": "totp"
    },
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Invalid Credentials):


{
    "status": "INVALID_CREDENTIALS",
    "message": "The provided credentials are incorrect.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The provided credentials are incorrect."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Invalid Credentials with CAPTCHA):


{
    "status": "INVALID_CREDENTIALS",
    "message": "The provided credentials are incorrect.",
    "data": {
        "captcha_required": true
    },
    "errors": [
        {
            "field": "email",
            "message": "The provided credentials are incorrect."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Account Deactivated):


{
    "status": "ACCOUNT_DEACTIVATED",
    "message": "Your account has been deactivated.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Your account has been deactivated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (403, Account Banned):


{
    "status": "ACCOUNT_BANNED",
    "message": "Your account has been banned.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Your account has been banned."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The email field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (423, Account Temporarily Locked):


{
    "status": "ACCOUNT_TEMPORARILY_LOCKED",
    "message": "Account temporarily locked. Try again in 15 minutes.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Account temporarily locked. Try again in 15 minutes."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (423, Account Locked for Review):


{
    "status": "ACCOUNT_LOCKED_FOR_REVIEW",
    "message": "Account locked pending admin review.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Account locked pending admin review."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string  optional    

User's email address (required if not using phone login). Example: boris@example.com

country_code   string  optional    

Country code with + prefix (required for phone login). Example: +43

phone_number   string  optional    

Phone number without country code (required for phone login). Example: 6641234567

password   string     

Plain-text password (min 8 characters). Example: SuperSecret123!

code   string  optional    

Optional 2FA one-time code (TOTP from authenticator app, or email OTP from a prior login attempt). When supplied together with credentials, the server completes the login in a single request — no separate challenge call needed. Example: 123456

recovery_code   string  optional    

Optional 2FA recovery code used in place of code when the authenticator device is unavailable. Example: ABCD-1234-EFGH

remember_me   boolean  optional    

Optional. When true, the issued refresh token is valid for 30 days instead of 7. Example: false

Social Login

Handles social login (Google / Apple) via Firebase ID token verification. On success returns a Sanctum token; if the linked user has 2FA enabled a limited challenge token is returned instead and the client must complete the 2FA challenge. Brand-new social users who have not yet recorded TERMS and PRIVACY consent receive a limited supplementary token and must call POST /auth/social/complete before accessing the feature surface.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/social-login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"firebase_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAifQ...\",
    \"provider\": \"google\",
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/social-login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "firebase_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAifQ...",
    "provider": "google",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/social-login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'firebase_token' => 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAifQ...',
            'provider' => 'google',
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/social-login');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "firebase_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAifQ...",
    "provider": "google",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Login successful.",
    "data": {
        "requires_supplementary": false,
        "token": "1|abcdef123456...",
        "refresh_token": "2|refreshtoken...",
        "expires_in": 900,
        "user": {
            "id": "usr-uuid",
            "first_name": "Max",
            "last_name": "Mustermann",
            "email": "max@example.com"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (200, Supplementary Required):


{
    "status": "SOCIAL_SUPPLEMENTARY_REQUIRED",
    "message": "Please complete registration to continue.",
    "data": {
        "requires_supplementary": true,
        "token": "1|limited-token...",
        "missing": [
            "last_name"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (401, 2FA Required):


{
    "status": "TWO_FACTOR_REQUIRED",
    "message": "Two-factor authentication is required.",
    "data": {
        "token": "2|challenge-token...",
        "two_factor_method": "totp"
    },
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (401, Invalid Token):


{
    "status": "FIREBASE_TOKEN_INVALID",
    "message": "The Firebase ID token is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "The Firebase ID token is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (401, Account Deactivated):


{
    "status": "ACCOUNT_DEACTIVATED",
    "message": "Your account has been deactivated.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Your account has been deactivated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (403, Account Banned):


{
    "status": "ACCOUNT_BANNED",
    "message": "Your account has been banned.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Your account has been banned."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "firebase_token",
            "message": "The firebase token field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/social-login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

firebase_token   string     

Firebase ID token from sign-in provider. Example: eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAifQ...

provider   string     

Social provider ("google" or "apple"). Allowed: google, apple. Example: google

code   string  optional    

Optional 2FA one-time code (TOTP or email OTP) for single-step social + 2FA login. When supplied, the server completes authentication in one request — no separate challenge call. Example: 123456

recovery_code   string  optional    

Optional 2FA recovery code in place of code. Example: ABCD-1234-EFGH

Refresh Token

requires authentication

Rotates the caller's refresh token. The presented refresh token is deleted and a new access + refresh token pair is returned. This endpoint does NOT require 2FA verification — the refresh token itself is the credential.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/refresh" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/refresh"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/refresh';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/refresh');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Token refreshed.",
    "data": {
        "token": "3|newaccess...",
        "refresh_token": "4|newrefresh...",
        "expires_in": 900
    },
    "errors": null,
    "meta": {}
}
 

Example response (401, Not a refresh token):


{
    "status": "UNAUTHORIZED",
    "message": "Invalid refresh token.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Invalid refresh token."
        }
    ],
    "meta": {}
}
 

Request      

POST api/v1/mobile/auth/refresh

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Complete Social Registration

requires authentication

Finalises consent capture and profile data for a social-login user who received requires_supplementary = true. Requires the limited social-supplementary token issued by POST /auth/social-login.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/social/complete" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"terms_document_id\": \"550e8400-e29b-41d4-a716-446655440000\",
    \"privacy_document_id\": \"550e8400-e29b-41d4-a716-446655440001\",
    \"processing_data_document_id\": \"550e8400-e29b-41d4-a716-446655440002\",
    \"marketing_consent\": false,
    \"first_name\": \"Max\",
    \"last_name\": \"Mustermann\",
    \"language\": \"de\",
    \"device_info\": {
        \"platform\": \"ios\",
        \"version\": \"17.0\"
    }
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/social/complete"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "terms_document_id": "550e8400-e29b-41d4-a716-446655440000",
    "privacy_document_id": "550e8400-e29b-41d4-a716-446655440001",
    "processing_data_document_id": "550e8400-e29b-41d4-a716-446655440002",
    "marketing_consent": false,
    "first_name": "Max",
    "last_name": "Mustermann",
    "language": "de",
    "device_info": {
        "platform": "ios",
        "version": "17.0"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/social/complete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'terms_document_id' => '550e8400-e29b-41d4-a716-446655440000',
            'privacy_document_id' => '550e8400-e29b-41d4-a716-446655440001',
            'processing_data_document_id' => '550e8400-e29b-41d4-a716-446655440002',
            'marketing_consent' => false,
            'first_name' => 'Max',
            'last_name' => 'Mustermann',
            'language' => 'de',
            'device_info' => ['platform' => 'ios', 'version' => '17.0'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/social/complete');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "terms_document_id": "550e8400-e29b-41d4-a716-446655440000",
    "privacy_document_id": "550e8400-e29b-41d4-a716-446655440001",
    "processing_data_document_id": "550e8400-e29b-41d4-a716-446655440002",
    "marketing_consent": false,
    "first_name": "Max",
    "last_name": "Mustermann",
    "language": "de",
    "device_info": {
        "platform": "ios",
        "version": "17.0"
    }
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Registration completed.",
    "data": {
        "token": "1|abcdef123456...",
        "refresh_token": "2|refreshtoken...",
        "expires_in": 900,
        "user": {
            "id": "usr-uuid",
            "first_name": "Max",
            "last_name": "Mustermann",
            "email": "max@example.com"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (403, Wrong token ability):


{
    "status": "FORBIDDEN",
    "message": "This action requires a social-supplementary token.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "This action requires a social-supplementary token."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "terms_document_id",
            "message": "The terms document id field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-12T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/social/complete

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

terms_document_id   string     

UUID of Terms version user accepted. Providing this ID IS the acceptance proof. Example: 550e8400-e29b-41d4-a716-446655440000

privacy_document_id   string     

UUID of Privacy Policy version user accepted. Providing this ID IS the acceptance proof. Example: 550e8400-e29b-41d4-a716-446655440001

processing_data_document_id   string     

UUID of Data Processing Consent version user accepted. Providing this ID IS the acceptance proof. Example: 550e8400-e29b-41d4-a716-446655440002

marketing_consent   boolean  optional    

optional Whether to opt in to marketing emails. Example: false

first_name   string  optional    

optional Fills the user's first name when empty. Example: Max

last_name   string  optional    

optional Fills the user's last name when empty. Example: Mustermann

language   string  optional    

optional Preferred language (de, en, hr). Allowed: de, en, hr. Example: de

device_info   object  optional    

optional Device metadata stored in consent audit trail.

platform   string  optional    

optional Device platform. Example: ios

version   string  optional    

optional Platform version. Example: 17.0

Verify Phone via Firebase

requires authentication

Verifies a phone number using a Firebase ID token. The mobile app uses Firebase Phone Auth to verify the user's phone number, then sends the Firebase ID token to this endpoint.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-phone" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"firebase_id_token\": \"eyJhbGciOiJSUzI1NiIs...\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-phone"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "firebase_id_token": "eyJhbGciOiJSUzI1NiIs..."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-phone';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'firebase_id_token' => 'eyJhbGciOiJSUzI1NiIs...',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/verify-phone');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "firebase_id_token": "eyJhbGciOiJSUzI1NiIs..."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Phone number verified successfully.",
    "data": {
        "phone_verified_at": "2026-06-11T12:00:00.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-11T12:00:00.000000Z"
    }
}
 

Example response (400, Phone Mismatch):


{
    "status": "ERROR",
    "message": "Phone number does not match.",
    "data": null,
    "errors": [
        {
            "field": "firebase_id_token",
            "message": "Phone number from Firebase does not match user profile."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-11T12:00:00.000000Z"
    }
}
 

Example response (401, Invalid Token):


{
    "status": "FIREBASE_TOKEN_INVALID",
    "message": "The Firebase ID token is invalid or has expired.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-11T12:00:00.000000Z"
    }
}
 

Example response (409, Phone Already Verified):


{
    "status": "ERROR",
    "message": "Phone number is already verified.",
    "data": null,
    "errors": [
        {
            "field": "firebase_id_token",
            "message": "Phone number is already verified."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-11T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/verify-phone

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

firebase_id_token   string     

The Firebase ID token from phone auth. Example: eyJhbGciOiJSUzI1NiIs...

Profile

Get User Profile

requires authentication

Returns the authenticated user's account profile including personal info, contact details, and two-factor authentication status.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/profile" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "id": "019fd2b7-f271-73fd-8268-848be381e136",
        "first_name": "Anna",
        "last_name": "Neuling",
        "email": "pending@demo.flexxr.at",
        "country_code": "+43",
        "phone_number": "6769876543",
        "status": "active",
        "avatar_url": null,
        "bio": null,
        "date_of_birth": null,
        "country": null,
        "city": null,
        "notify_push": true,
        "notify_email": true,
        "notify_sms": true,
        "notify_marketing": false,
        "email_verified_at": "2026-08-05T16:18:32+00:00",
        "phone_verified_at": "2026-08-05T16:18:32+00:00",
        "created_at": "2026-08-05T16:18:18+00:00",
        "updated_at": "2026-08-05T16:18:33+00:00",
        "employee_profile": null
    },
    "errors": null,
    "meta": {
        "request_id": "930c5193-62e0-4ecd-953f-e1066486631b",
        "timestamp": "2026-08-05T16:18:34.713826Z"
    }
}
 

Request      

GET api/v1/mobile/profile

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Change User Password

requires authentication

Changes the authenticated user's password.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/password" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"current_password\": \"S3cure-Passw0rd!\",
    \"password\": \"S3cure-Passw0rd!\",
    \"logout_other_devices\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/password"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "current_password": "S3cure-Passw0rd!",
    "password": "S3cure-Passw0rd!",
    "logout_other_devices": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/password';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'current_password' => 'S3cure-Passw0rd!',
            'password' => 'S3cure-Passw0rd!',
            'logout_other_devices' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/password');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "current_password": "S3cure-Passw0rd!",
    "password": "S3cure-Passw0rd!",
    "logout_other_devices": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password changed successfully.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "3f784394-7936-45b9-9fb2-0f9bc3e527cb",
        "timestamp": "2026-08-05T16:18:35.373354Z"
    }
}
 

Request      

PUT api/v1/mobile/profile/password

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

current_password   string     

Example: S3cure-Passw0rd!

password   string     

Example: S3cure-Passw0rd!

logout_other_devices   boolean  optional    

nullable Revoke all other active sessions if true. Example: true

Upload Profile Photo

requires authentication

Upload (or replace) the authenticated worker's profile photo.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/photo" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "photo=@/tmp/phpi3ec0op4vs6d2HbU8Nl" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/photo"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('photo', document.querySelector('input[name="photo"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/photo';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'photo',
                'contents' => fopen('/tmp/phpi3ec0op4vs6d2HbU8Nl', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/photo');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.files.add(await http.MultipartFile.fromPath('photo', '/tmp/phpi3ec0op4vs6d2HbU8Nl'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your profile photo has been updated.",
    "data": {
        "avatar_url": "/storage/avatars/employees/BlRaQOE7e1f5iXoxW40ek9H2IYGSe998YjY9LItd.jpg"
    },
    "errors": null,
    "meta": {
        "request_id": "c3d2c124-78c4-49c6-beac-5278c8e18aed",
        "timestamp": "2026-08-05T16:18:35.399573Z"
    }
}
 

Example response (422, Not an image):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "photo",
            "message": "The photo must be an image."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/profile/photo

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

photo   file     

Example: /tmp/phpi3ec0op4vs6d2HbU8Nl

Request Email Change

requires authentication

Request an email change with verification to the new email.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/email/request-change" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"new_email\": \"name@example.at\",
    \"password\": \"S3cure-Passw0rd!\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/email/request-change"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "new_email": "name@example.at",
    "password": "S3cure-Passw0rd!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/email/request-change';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'new_email' => 'name@example.at',
            'password' => 'S3cure-Passw0rd!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/email/request-change');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "new_email": "name@example.at",
    "password": "S3cure-Passw0rd!"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Verification email sent. Please check your inbox to confirm the change.",
    "data": {
        "pending_email": "ganz.neue@example.at",
        "expires_at": "2026-08-05T16:48:35+00:00",
        "verification_sent_to": "ganz.neue@example.at"
    },
    "errors": null,
    "meta": {
        "request_id": "f0798fcd-a4d3-4ab4-b502-97285b1866f5",
        "timestamp": "2026-08-05T16:18:35.641680Z"
    }
}
 

Example response (409, Address already in use):


{
    "status": "EMAIL_ALREADY_EXISTS",
    "message": "That email address is already taken.",
    "data": null,
    "errors": [
        {
            "field": "new_email",
            "message": "That email address is already taken."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/profile/email/request-change

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

new_email   string     

Example: name@example.at

password   string     

Example: S3cure-Passw0rd!

Cancel Pending Email

requires authentication

Cancel a pending email change request.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/email/pending" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/email/pending"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/email/pending';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/email/pending');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The pending email change has been cancelled.",
    "data": {
        "cancelled_email": "neue.adresse@example.at",
        "current_email": "pending@demo.flexxr.at"
    },
    "errors": null,
    "meta": {
        "request_id": "1afeaa90-82ab-4363-8d65-679ca592a11a",
        "timestamp": "2026-08-05T16:18:35.656630Z"
    }
}
 

Request      

DELETE api/v1/mobile/profile/email/pending

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Set Phone Number

requires authentication

Add a phone number (country code + local number, as in registration) to an account whose phone has never been verified — typically a social-login account. Refused once a verified number exists; use the phone-change flow instead.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "country_code": "+43",
    "phone_number": "6641234567"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/phone';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'country_code' => '+43',
            'phone_number' => '6641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/phone');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "country_code": "+43",
    "phone_number": "6641234567"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Saved):


{
    "status": "SUCCESS",
    "message": "Phone number saved. Please verify it.",
    "data": {
        "country_code": "+43",
        "phone_number": "6641234567",
        "phone": "+436641234567",
        "phone_verified": false,
        "next_step": "phone_verification"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-08-13T10:00:00.000000Z"
    }
}
 

Example response (409, Already verified):


{
    "status": "INVALID_OPERATION",
    "message": "Your phone number is verified. Use the phone change flow to replace it.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-08-13T10:00:00.000000Z"
    }
}
 

Example response (409, Number taken):


{
    "status": "PHONE_ALREADY_EXISTS",
    "message": "This phone number is already registered to another account.",
    "data": null,
    "errors": [
        {
            "field": "phone_number",
            "message": "This phone number is already registered to another account."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-08-13T10:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/profile/phone

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

country_code   string     

Required. Phone country code (E.164 prefix). Must match the regex /^+[1-9]\d{0,3}$/. Must not be greater than 5 characters. Example: +43

phone_number   string     

Required. Local phone number (digits only). Must match the regex /^\d+$/. Must not be greater than 15 characters. Example: 6641234567

Request Phone Change

requires authentication

Request a phone number change.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/request-change" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"country_code\": \"AT\",
    \"phone_number\": \"6641234567\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/request-change"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "country_code": "AT",
    "phone_number": "6641234567"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/request-change';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'country_code' => 'AT',
            'phone_number' => '6641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/request-change');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "country_code": "AT",
    "phone_number": "6641234567"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Code sent):


{
    "status": "SUCCESS",
    "message": "We sent a verification code to the new number.",
    "data": {
        "pending_phone": "+436641234567",
        "expires_at": "2026-07-28T10:10:00+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/profile/phone/request-change

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

country_code   string     

Example: AT

phone_number   string     

Example: 6641234567

Verify Phone Change

requires authentication

Complete a phone number change using a Firebase Phone Auth ID token.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/verify" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"firebase_id_token\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/verify"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "firebase_id_token": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'firebase_id_token' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/verify');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "firebase_id_token": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Verified):


{
    "status": "SUCCESS",
    "message": "Phone number updated.",
    "data": {
        "phone_number": "6641234567",
        "country_code": "+43",
        "phone_verified_at": "2026-07-28T10:05:00+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Example response (422, Wrong code):


{
    "status": "VALIDATION_ERROR",
    "message": "That code is not valid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "That code is not valid."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/profile/phone/verify

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

firebase_id_token   string     

Example: Beispieltext

Cancel Pending Phone

requires authentication

Cancel a pending phone change request.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/pending" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/pending"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/pending';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/profile/phone/pending');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The pending phone change has been cancelled.",
    "data": {
        "cancelled_phone": "+436641239876",
        "current_phone": "+436769876543"
    },
    "errors": null,
    "meta": {
        "request_id": "64b580b3-bf62-4a88-9be6-28b5ed633dcc",
        "timestamp": "2026-08-05T16:18:35.683946Z"
    }
}
 

Request      

DELETE api/v1/mobile/profile/phone/pending

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Documents

List My Documents

requires authentication

Everything the worker has uploaded, with each document's review state — pending, verified, rejected, reupload_requested, expired or superseded — and the reviewer's note where one was left.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "documents": [
            {
                "id": "019fd2b7-f280-73fe-83af-4fdd5f9db332",
                "type": "id_card",
                "type_label": "ID card",
                "original_filename": "ausweis.pdf",
                "status": "pending",
                "status_label": "Ausstehend",
                "status_color": "yellow",
                "rejection_reason": null,
                "expires_at": null,
                "is_expired": false,
                "expires_soon": false,
                "is_valid": false,
                "created_at": "2026-08-05 16:18:18"
            },
            {
                "id": "019fd2b8-28ce-735c-aa32-c03c49655e69",
                "type": "passport",
                "type_label": "Passport",
                "original_filename": "veniam.pdf",
                "status": "verified",
                "status_label": "Verifiziert",
                "status_color": "green",
                "rejection_reason": null,
                "expires_at": "2033-05-22",
                "is_expired": false,
                "expires_soon": false,
                "is_valid": true,
                "created_at": "2026-08-05 16:18:32"
            }
        ],
        "counts": {
            "total": 2,
            "pending": 1,
            "verified": 1,
            "rejected": 0,
            "expiring_soon": 0
        }
    }
}
 

Request      

GET api/v1/mobile/documents

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Document Checklist

requires authentication

Which documents this worker must provide before their profile can be submitted, and whether each is satisfied. The set is not fixed: a third-country national is additionally asked for work authorisation, resolved from the residence status on their profile.

items holds both kinds in one list, required ones first, each marked with required:

accepts lists the document types that satisfy the item — several for a required slot (identity takes a passport or an ID card), exactly one for an optional entry. Those values are what POST /mobile/documents takes as type and what a job requirement names, so an upload made from this list satisfies the matching requirement with no further mapping.

document is the file answering the item, in the same shape GET /mobile/documents returns, or null when nothing has been uploaded against it. Where a worker holds several of one type the most relevant wins, so a replacement outranks the rejection it replaced.

Note that satisfied means "on file", which includes a document still under review — document.status is what separates pending from verified, and only a verified one counts towards a job requirement.

counts summarises the worker's uploads and matches GET /mobile/documents exactly, so the screen needs one call rather than two.

This is the account-level checklist. For what a particular job demands, use GET /jobs/{jobId}/requirements.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/documents/checklist" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/checklist"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents/checklist';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents/checklist');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The document checklist has been retrieved.",
    "data": {
        "ready": false,
        "items": [
            {
                "key": "identity",
                "label": "Reisepass oder Personalausweis",
                "accepts": [
                    "passport",
                    "id_card"
                ],
                "required": true,
                "satisfied": true,
                "document": {
                    "id": "019fd2b8-28ce-735c-aa32-c03c49655e69",
                    "type": "passport",
                    "type_label": "Passport",
                    "original_filename": "veniam.pdf",
                    "status": "verified",
                    "status_label": "Verifiziert",
                    "status_color": "green",
                    "rejection_reason": null,
                    "expires_at": "2033-05-22",
                    "is_expired": false,
                    "expires_soon": false,
                    "is_valid": true,
                    "created_at": "2026-08-05 16:18:32"
                }
            },
            {
                "key": "address_proof",
                "label": "Meldezettel",
                "accepts": [
                    "proof_of_address"
                ],
                "required": true,
                "satisfied": false,
                "document": null
            },
            {
                "key": "social_insurance",
                "label": "e-card (Sozialversicherung)",
                "accepts": [
                    "social_insurance_card"
                ],
                "required": true,
                "satisfied": false,
                "document": null
            },
            {
                "key": "bank",
                "label": "Kontoauszug",
                "accepts": [
                    "bank_statement"
                ],
                "required": true,
                "satisfied": false,
                "document": null
            },
            {
                "key": "driver_license",
                "label": "Driver's licence",
                "accepts": [
                    "driver_license"
                ],
                "required": false,
                "satisfied": false,
                "document": null
            },
            {
                "key": "certification",
                "label": "Certification",
                "accepts": [
                    "certification"
                ],
                "required": false,
                "satisfied": false,
                "document": null
            },
            {
                "key": "health_certificate",
                "label": "Health certificate",
                "accepts": [
                    "health_certificate"
                ],
                "required": false,
                "satisfied": false,
                "document": null
            },
            {
                "key": "criminal_record_check",
                "label": "Criminal record check",
                "accepts": [
                    "criminal_record_check"
                ],
                "required": false,
                "satisfied": false,
                "document": null
            },
            {
                "key": "tax_document",
                "label": "Tax document",
                "accepts": [
                    "tax_document"
                ],
                "required": false,
                "satisfied": false,
                "document": null
            }
        ],
        "counts": {
            "total": 2,
            "pending": 1,
            "verified": 1,
            "rejected": 0,
            "expiring_soon": 0
        }
    }
}
 

Request      

GET api/v1/mobile/documents/checklist

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Upload a Document

requires authentication

Submits one of the documents the profile or a job requires. The type matches the requirement_code returned by GET /jobs/{jobId}/requirements, so the app can pass it straight through from the row the worker tapped.

Uploading does not satisfy a requirement on its own — the document lands as pending and an administrator verifies it. Until then the checklist still reports the item as outstanding, and applications that depend on it are still refused.

Two-sided documents may send file_back alongside file.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "type=social_insurance_card"\
    --form "document_number=AT1234567"\
    --form "issued_at=2024-03-01"\
    --form "expires_at=2034-03-01"\
    --form "issuing_country=AT"\
    --form "issuing_authority=Magistrat Wien"\
    --form "file=@/tmp/php7ob920j5k99q5t0Rmk6" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('type', 'social_insurance_card');
body.append('document_number', 'AT1234567');
body.append('issued_at', '2024-03-01');
body.append('expires_at', '2034-03-01');
body.append('issuing_country', 'AT');
body.append('issuing_authority', 'Magistrat Wien');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'type',
                'contents' => 'social_insurance_card'
            ],
            [
                'name' => 'document_number',
                'contents' => 'AT1234567'
            ],
            [
                'name' => 'issued_at',
                'contents' => '2024-03-01'
            ],
            [
                'name' => 'expires_at',
                'contents' => '2034-03-01'
            ],
            [
                'name' => 'issuing_country',
                'contents' => 'AT'
            ],
            [
                'name' => 'issuing_authority',
                'contents' => 'Magistrat Wien'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/php7ob920j5k99q5t0Rmk6', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.fields['type'] = 'social_insurance_card';
  request.fields['document_number'] = 'AT1234567';
  request.fields['issued_at'] = '2024-03-01';
  request.fields['expires_at'] = '2034-03-01';
  request.fields['issuing_country'] = 'AT';
  request.fields['issuing_authority'] = 'Magistrat Wien';
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/php7ob920j5k99q5t0Rmk6'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The document has been uploaded successfully.",
    "data": {
        "document": {
            "id": "019fd2b8-672d-7348-ae17-4de67610d176",
            "type": "work_permit",
            "type_label": "Work permit",
            "original_filename": "reisepass.pdf",
            "status": "pending",
            "status_label": "Ausstehend",
            "status_color": "yellow",
            "rejection_reason": null,
            "expires_at": "2027-08-05",
            "is_expired": false,
            "expires_soon": false,
            "is_valid": false,
            "created_at": "2026-08-05 16:18:48",
            "has_back_side": false
        }
    }
}
 

Example response (422, Rejected file):


{
    "status": "VALIDATION_ERROR",
    "message": "The file must be a PDF or image and no larger than the configured limit."
}
 

Request      

POST api/v1/mobile/documents

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

type   string     

The document type, matching the requirement code. Allowed: passport, id_card, driver_license, work_permit, residence_permit, rot_weiss_rot_karte, blue_card, bank_statement, proof_of_address, social_insurance_card, tax_document, certification, health_certificate, criminal_record_check, other. Example: social_insurance_card

file   file     

The scan or photograph. PDF, JPEG, PNG or WebP. Example: /tmp/php7ob920j5k99q5t0Rmk6

file_back   file  optional    

The reverse side, for cards that have one.

document_number   string  optional    

Number printed on the document. Example: AT1234567

issued_at   date  optional    

Date of issue; cannot be in the future. Example: 2024-03-01

expires_at   date  optional    

Expiry date. Required for work-authorisation documents — permits, Rot-Weiß-Rot Karte, EU Blue Card. Example: 2034-03-01

issuing_country   string  optional    

Two-letter ISO code of the issuing state. Example: AT

issuing_authority   string  optional    

Authority that issued it. Example: Magistrat Wien

Get a Document

requires authentication

One document with its review state and, when a reviewer rejected it, the reason they gave.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "document": {
            "id": "019fd2b8-6103-7134-bdc4-ac4ec15c8ab5",
            "type": "passport",
            "type_label": "Passport",
            "original_filename": "reisepass.pdf",
            "mime_type": "application/pdf",
            "file_size": 131072,
            "status": "ocr_failed",
            "status_label": "OCR fehlgeschlagen",
            "status_color": "orange",
            "rejection_reason": null,
            "document_number": null,
            "issued_at": null,
            "expires_at": null,
            "issuing_country": null,
            "issuing_authority": null,
            "is_expired": false,
            "expires_soon": false,
            "is_valid": false,
            "reviewed_at": null,
            "created_at": "2026-08-05 16:18:46",
            "download_url": "http://localhost:8001/api/v1/mobile/documents/019fd2b8-6103-7134-bdc4-ac4ec15c8ab5/download",
            "has_back_side": false
        }
    }
}
 

Request      

GET api/v1/mobile/documents/{documentId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

documentId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Download a Document

requires authentication

Streams the worker's own file. The link is short-lived by design — a document contains identity data and should not sit in a shareable URL.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/download" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/download"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/download';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/download');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "948c444a-46c2-4c6e-b672-2eb6ffadfda0",
        "timestamp": "2026-08-21T05:14:33.114939Z"
    }
}
 

Request      

GET api/v1/mobile/documents/{documentId}/download

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

documentId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Replace a Document

requires authentication

Supersedes an existing document — used after a reviewer rejects one or asks for a fresh copy, and when the original has expired. The previous version is archived rather than deleted, so the review history stays intact, and the replacement enters pending for verification.

The type is taken from the document being replaced and cannot be changed; upload a new document instead. Everything else the upload endpoint accepts is accepted here, file_back included — a two-sided document replaced without its reverse side would lose it.

expires_at is required when replacing a work-authorisation document (permit, Rot-Weiß-Rot Karte, EU Blue Card): that date decides whether its holder may still be sent to a shift, and one without it counts as expired.

This is a POST with a multipart/form-data body. It also needs an Idempotency-Key header; replaying the same key returns the first result rather than superseding the document twice.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "document_number=AT1234567"\
    --form "issued_at=2024-03-01"\
    --form "expires_at=2034-03-01"\
    --form "issuing_country=AT"\
    --form "issuing_authority=Magistrat Wien"\
    --form "file=@/tmp/phpiob44qtm7pg23rOcGlH" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('document_number', 'AT1234567');
body.append('issued_at', '2024-03-01');
body.append('expires_at', '2034-03-01');
body.append('issuing_country', 'AT');
body.append('issuing_authority', 'Magistrat Wien');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'document_number',
                'contents' => 'AT1234567'
            ],
            [
                'name' => 'issued_at',
                'contents' => '2024-03-01'
            ],
            [
                'name' => 'expires_at',
                'contents' => '2034-03-01'
            ],
            [
                'name' => 'issuing_country',
                'contents' => 'AT'
            ],
            [
                'name' => 'issuing_authority',
                'contents' => 'Magistrat Wien'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/phpiob44qtm7pg23rOcGlH', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.fields['document_number'] = 'AT1234567';
  request.fields['issued_at'] = '2024-03-01';
  request.fields['expires_at'] = '2034-03-01';
  request.fields['issuing_country'] = 'AT';
  request.fields['issuing_authority'] = 'Magistrat Wien';
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/phpiob44qtm7pg23rOcGlH'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The document has been re-uploaded and is awaiting review.",
    "data": {
        "document": {
            "id": "019fd2b8-36f1-70c6-8a88-6e076134cc2a",
            "type": "id_card",
            "type_label": "ID card",
            "original_filename": "beispiel.pdf",
            "status": "pending",
            "status_label": "Ausstehend",
            "status_color": "yellow",
            "rejection_reason": null,
            "expires_at": "2034-03-01",
            "is_expired": false,
            "expires_soon": false,
            "is_valid": false,
            "created_at": "2026-08-05 16:18:35",
            "supersedes": "019fd2b7-f280-73fe-83af-4fdd5f9db332"
        }
    }
}
 

Request      

POST api/v1/mobile/documents/{documentId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

documentId   string     

The document being replaced. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

file   file     

The new scan or photograph. PDF, JPEG, PNG or WebP. Example: /tmp/phpiob44qtm7pg23rOcGlH

file_back   file  optional    

The reverse side, for cards that have one.

document_number   string  optional    

Number printed on the document. Example: AT1234567

issued_at   date  optional    

Date of issue; cannot be in the future. Example: 2024-03-01

expires_at   date  optional    

Expiry date. Required when replacing a work-authorisation document. Example: 2034-03-01

issuing_country   string  optional    

Two-letter ISO code of the issuing state. Example: AT

issuing_authority   string  optional    

Authority that issued it. Example: Magistrat Wien

Delete a Document

requires authentication

Removes a document the worker uploaded. A verified document that a requirement depends on cannot simply be dropped — replace it instead, so the profile does not silently fall out of compliance.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The document has been deleted successfully."
}
 

Request      

DELETE api/v1/mobile/documents/{documentId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

documentId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

List My Attachments

requires authentication

Files the worker has supplied that the platform does not review — a reference, a course certificate, anything a company asked to see that the document types do not name.

These are not documents. They carry no review status and no expiry, a worker may hold as many as they like, and none of them satisfies a job requirement — nothing has checked what they contain. Use GET /mobile/documents for the reviewed set.

They share the worker's storage quota with documents, so quota is returned alongside: used_bytes split by what is consuming it, against the same limit_bytes an upload is refused for exceeding.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/attachments" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/attachments"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/attachments';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/attachments');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "attachments": [
            {
                "id": "019fd2b8-29e0-72e1-be30-9be100181d40",
                "label": "voluptas nihil",
                "original_filename": "est.pdf",
                "mime_type": "application/pdf",
                "file_size": 293696,
                "scanned_at": "2026-08-05T16:18:32+00:00",
                "created_at": "2026-08-05 16:18:32"
            }
        ],
        "quota": {
            "used_bytes": 1270224,
            "documents_bytes": 976528,
            "attachments_bytes": 293696,
            "limit_bytes": 104857600,
            "available_bytes": 103587376
        }
    }
}
 

Request      

GET api/v1/mobile/attachments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Add an Attachment

requires authentication

Supplies a file the platform does not review: a reference, a course certificate, whatever a company asked to see that the document types do not name. label says what it is, and is required — a reviewer looking at an application cannot act on an untitled file.

A worker may hold as many as they like, including several with the same label. Nothing here expires, nothing enters the review queue, and an attachment never satisfies a job requirement — the platform has not checked it. Use POST /mobile/documents for anything that has to be verified.

The file is still scanned for malware, and still counts against the same storage quota as documents; an infected file is removed rather than rejected, since there is no review state to move it to.

Same size and format limits as a document: PDF, JPEG, PNG or WebP.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/attachments" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "label=Staplerschein"\
    --form "file=@/tmp/phpdqrl4bufd3tvc9m5wFw" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/attachments"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('label', 'Staplerschein');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/attachments';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'label',
                'contents' => 'Staplerschein'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/phpdqrl4bufd3tvc9m5wFw', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/attachments');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.fields['label'] = 'Staplerschein';
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/phpdqrl4bufd3tvc9m5wFw'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The attachment has been added.",
    "data": {
        "attachment": {
            "id": "019fd2b8-3720-719d-87d9-bb06b1cafe16",
            "label": "Staplerschein",
            "original_filename": "beispiel.pdf",
            "mime_type": "application/pdf",
            "file_size": 65536,
            "scanned_at": null,
            "created_at": "2026-08-05 16:18:35"
        },
        "quota": {
            "used_bytes": 1335760,
            "documents_bytes": 976528,
            "attachments_bytes": 359232,
            "limit_bytes": 104857600,
            "available_bytes": 103521840
        }
    }
}
 

Example response (422, Quota exhausted):


{
    "status": "INVALID_OPERATION",
    "message": "The storage quota has been reached.",
    "data": {
        "quota": {
            "used_bytes": 104857600,
            "documents_bytes": 94371840,
            "attachments_bytes": 10485760,
            "limit_bytes": 104857600,
            "available_bytes": 0
        }
    }
}
 

Request      

POST api/v1/mobile/attachments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

label   string     

What the file is. Example: Staplerschein

file   file     

The file itself. PDF, JPEG, PNG or WebP. Example: /tmp/phpdqrl4bufd3tvc9m5wFw

Delete an Attachment

requires authentication

Removes a file the worker attached, freeing the storage it occupied. Nothing depends on an attachment — it satisfies no requirement and is part of no review — so it is deleted outright rather than superseded the way a document is.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/attachments/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/attachments/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/attachments/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/attachments/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The attachment has been removed."
}
 

Example response (404, Not found):


{
    "status": "RESOURCE_NOT_FOUND",
    "message": "The attachment was not found."
}
 

Request      

DELETE api/v1/mobile/attachments/{attachmentId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

attachmentId   string     

The attachment to remove. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Jobs

List Job Categories

Returns all active job categories in a hierarchical structure. Root categories are returned at the top level; their active children are nested under the children key.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/job-categories?include_children=1&featured_only=" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/job-categories"
);

const params = {
    "include_children": "1",
    "featured_only": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/job-categories';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'include_children' => '1',
            'featured_only' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/job-categories')
      .replace(queryParameters: {
        'include_children': '1',
        'featured_only': '',
      });

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "categories": [
            {
                "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                "slug": "gastro",
                "name": "Gastronomie",
                "name_en": "Gastronomy",
                "name_localized": "Gastronomy",
                "icon": "utensils",
                "color": null,
                "kollektivvertrag": "Gastgewerbe",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 1,
                "children": []
            },
            {
                "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                "slug": "hotel",
                "name": "Hotellerie",
                "name_en": "Hospitality",
                "name_localized": "Hospitality",
                "icon": "hotel",
                "color": null,
                "kollektivvertrag": "Hotel- und Gastgewerbe",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 2,
                "children": []
            },
            {
                "id": "019fd2b7-f377-7108-8400-efa89715a39f",
                "slug": "food_service",
                "name": "Food Service",
                "name_en": "Food Service",
                "name_localized": "Food Service",
                "icon": "utensils",
                "color": null,
                "kollektivvertrag": "Gastgewerbe",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 3,
                "children": []
            },
            {
                "id": "019fd2b7-f37a-72a9-84a9-7862f636e2fd",
                "slug": "catering",
                "name": "Catering",
                "name_en": "Catering",
                "name_localized": "Catering",
                "icon": "utensils",
                "color": null,
                "kollektivvertrag": "Gastgewerbe",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 4,
                "children": []
            },
            {
                "id": "019fd2b7-f37d-712d-92b3-aa5540f6b2a7",
                "slug": "retail",
                "name": "Einzelhandel",
                "name_en": "Retail",
                "name_localized": "Retail",
                "icon": "shopping-cart",
                "color": null,
                "kollektivvertrag": "Handel",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 5,
                "children": []
            },
            {
                "id": "019fd2b7-f380-7300-91c6-75c968e6d045",
                "slug": "supermarket",
                "name": "Lebensmittelhandel",
                "name_en": "Grocery",
                "name_localized": "Grocery",
                "icon": "shopping-cart",
                "color": null,
                "kollektivvertrag": "Handel",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 6,
                "children": []
            },
            {
                "id": "019fd2b7-f384-7071-b096-674d16b4ad00",
                "slug": "pharmacy",
                "name": "Apotheke",
                "name_en": "Pharmacy",
                "name_localized": "Pharmacy",
                "icon": "heart-pulse",
                "color": null,
                "kollektivvertrag": "Pharmazeutischer Großhandel",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 7,
                "children": []
            },
            {
                "id": "019fd2b7-f388-7098-b463-ae170338e60f",
                "slug": "production",
                "name": "Produktion",
                "name_en": "Production",
                "name_localized": "Production",
                "icon": "industry",
                "color": null,
                "kollektivvertrag": "Industrie",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 8,
                "children": []
            },
            {
                "id": "019fd2b7-f38b-7209-8b83-b345b172def2",
                "slug": "warehouse",
                "name": "Lager",
                "name_en": "Warehouse",
                "name_localized": "Warehouse",
                "icon": "industry",
                "color": null,
                "kollektivvertrag": "Industrie",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 9,
                "children": []
            },
            {
                "id": "019fd2b7-f38e-7255-bfe3-0f010aca0086",
                "slug": "logistics",
                "name": "Logistik",
                "name_en": "Logistics",
                "name_localized": "Logistics",
                "icon": "truck",
                "color": null,
                "kollektivvertrag": "Industrie",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 10,
                "children": []
            },
            {
                "id": "019fd2b7-f391-727d-9871-61207ae27ab3",
                "slug": "manufacturing",
                "name": "Fertigung",
                "name_en": "Manufacturing",
                "name_localized": "Manufacturing",
                "icon": "industry",
                "color": null,
                "kollektivvertrag": "Industrie",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 11,
                "children": []
            },
            {
                "id": "019fd2b7-f394-7015-b80e-5fa954b27658",
                "slug": "transportation",
                "name": "Transport",
                "name_en": "Transportation",
                "name_localized": "Transportation",
                "icon": "truck",
                "color": null,
                "kollektivvertrag": "Güterbeförderungsgewerbe",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 12,
                "children": []
            },
            {
                "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                "slug": "event",
                "name": "Events",
                "name_en": "Events",
                "name_localized": "Events",
                "icon": "calendar-star",
                "color": null,
                "kollektivvertrag": "Veranstaltungsdienstleistung",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 13,
                "children": []
            },
            {
                "id": "019fd2b7-f39c-7064-8f21-15054bddd646",
                "slug": "security",
                "name": "Security",
                "name_en": "Security",
                "name_localized": "Security",
                "icon": "shield",
                "color": null,
                "kollektivvertrag": "Bewachungsgewerbe",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 14,
                "children": []
            },
            {
                "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                "slug": "office",
                "name": "Büro",
                "name_en": "Office",
                "name_localized": "Office",
                "icon": "briefcase",
                "color": null,
                "kollektivvertrag": "Handel/Büro",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 15,
                "children": []
            },
            {
                "id": "019fd2b7-f3a2-7147-883f-c3207db81b51",
                "slug": "reception",
                "name": "Empfang",
                "name_en": "Reception",
                "name_localized": "Reception",
                "icon": "briefcase",
                "color": null,
                "kollektivvertrag": "Handel/Büro",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 16,
                "children": []
            },
            {
                "id": "019fd2b7-f3a5-70a8-a3b1-9140f01abf8e",
                "slug": "call_center",
                "name": "Call Center",
                "name_en": "Call Center",
                "name_localized": "Call Center",
                "icon": "briefcase",
                "color": null,
                "kollektivvertrag": "Handel/Büro",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 17,
                "children": []
            },
            {
                "id": "019fd2b7-f3a8-72f6-8ad1-0f499f6816e1",
                "slug": "customer_service",
                "name": "Kundenservice",
                "name_en": "Customer Service",
                "name_localized": "Customer Service",
                "icon": "briefcase",
                "color": null,
                "kollektivvertrag": "Handel/Büro",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 18,
                "children": []
            },
            {
                "id": "019fd2b7-f3ab-70a0-9523-b474e2007212",
                "slug": "software",
                "name": "Software",
                "name_en": "Software",
                "name_localized": "Software",
                "icon": "code",
                "color": null,
                "kollektivvertrag": "IT-KV",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 19,
                "children": []
            },
            {
                "id": "019fd2b7-f3c2-73b2-9873-51431d14404e",
                "slug": "it_services",
                "name": "IT Services",
                "name_en": "IT Services",
                "name_localized": "IT Services",
                "icon": "code",
                "color": null,
                "kollektivvertrag": "IT-KV",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 20,
                "children": []
            },
            {
                "id": "019fd2b7-f3cc-728f-a1eb-11d5120b3dca",
                "slug": "cleaning",
                "name": "Reinigung",
                "name_en": "Cleaning",
                "name_localized": "Cleaning",
                "icon": "broom",
                "color": null,
                "kollektivvertrag": "Gebäudereinigung",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 21,
                "children": []
            },
            {
                "id": "019fd2b7-f3d1-72e1-a261-10207b6d759d",
                "slug": "facility",
                "name": "Facility Management",
                "name_en": "Facility Management",
                "name_localized": "Facility Management",
                "icon": "broom",
                "color": null,
                "kollektivvertrag": "Gebäudereinigung",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 22,
                "children": []
            },
            {
                "id": "019fd2b7-f3d5-71b4-bece-4ea026852495",
                "slug": "care",
                "name": "Pflege/Betreuung",
                "name_en": "Care",
                "name_localized": "Care",
                "icon": "heart-pulse",
                "color": null,
                "kollektivvertrag": "Sozialwirtschaft",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 23,
                "children": []
            },
            {
                "id": "019fd2b7-f3d8-72d5-842e-c1f00337e9ec",
                "slug": "healthcare",
                "name": "Gesundheitswesen",
                "name_en": "Healthcare",
                "name_localized": "Healthcare",
                "icon": "heart-pulse",
                "color": null,
                "kollektivvertrag": "Sozialwirtschaft",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 24,
                "children": []
            },
            {
                "id": "019fd2b7-f3dc-70cb-a662-0fc23b0ec307",
                "slug": "construction",
                "name": "Bau (Hilfstätigkeiten)",
                "name_en": "Construction",
                "name_localized": "Construction",
                "icon": "building",
                "color": null,
                "kollektivvertrag": "Bauindustrie",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 25,
                "children": []
            },
            {
                "id": "019fd2b7-f3df-70bb-8881-36c5f2234495",
                "slug": "real_estate",
                "name": "Immobilien",
                "name_en": "Real Estate",
                "name_localized": "Real Estate",
                "icon": "building",
                "color": null,
                "kollektivvertrag": "Bauindustrie",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 26,
                "children": []
            },
            {
                "id": "019fd2b7-f3e5-714f-8db2-a5ae4350ae5c",
                "slug": "graphic_design",
                "name": "Grafikdesign",
                "name_en": "Graphic Design",
                "name_localized": "Graphic Design",
                "icon": "palette",
                "color": null,
                "kollektivvertrag": "Werbewirtschaft",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 27,
                "children": []
            },
            {
                "id": "019fd2b7-f3e9-71c6-b382-7451adaf4bb7",
                "slug": "media",
                "name": "Medien",
                "name_en": "Media",
                "name_localized": "Media",
                "icon": "palette",
                "color": null,
                "kollektivvertrag": "Werbewirtschaft",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 28,
                "children": []
            },
            {
                "id": "019fd2b7-f3ec-717a-ac4d-13011f3096f1",
                "slug": "education",
                "name": "Bildung",
                "name_en": "Education",
                "name_localized": "Education",
                "icon": "graduation-cap",
                "color": null,
                "kollektivvertrag": "Bildungseinrichtungen",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 29,
                "children": []
            },
            {
                "id": "019fd2b7-f3f0-70a8-b7be-6be4c17c9429",
                "slug": "finance",
                "name": "Finanzen",
                "name_en": "Finance",
                "name_localized": "Finance",
                "icon": "chart-line",
                "color": null,
                "kollektivvertrag": "Banken und Versicherungen",
                "description": null,
                "is_featured": true,
                "parent_id": null,
                "sort_order": 30,
                "children": []
            },
            {
                "id": "019fd2b7-f3f3-737d-9abe-5edb92ceb02a",
                "slug": "agriculture",
                "name": "Landwirtschaft",
                "name_en": "Agriculture",
                "name_localized": "Agriculture",
                "icon": "leaf",
                "color": null,
                "kollektivvertrag": "Land- und Forstwirtschaft",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 31,
                "children": []
            },
            {
                "id": "019fd2b7-f3f6-70c7-868b-6ac419f0d02c",
                "slug": "other",
                "name": "Sonstiges",
                "name_en": "Other",
                "name_localized": "Other",
                "icon": "ellipsis",
                "color": null,
                "kollektivvertrag": "Arbeitskräfteüberlasser",
                "description": null,
                "is_featured": false,
                "parent_id": null,
                "sort_order": 32,
                "children": []
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/job-categories

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

include_children   boolean  optional    

Include child categories nested under each root (default true). Example: true

featured_only   boolean  optional    

Return only featured root categories. Example: false

List Available Jobs

Returns available jobs with comprehensive filtering options. Supports location (state/radius), category, rate range, shift type, and sorting.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/jobs?sort=highest_salary&exclude_applied=1&per_page=15" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs"
);

const params = {
    "sort": "highest_salary",
    "exclude_applied": "1",
    "per_page": "15",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'sort' => 'highest_salary',
            'exclude_applied' => '1',
            'per_page' => '15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs')
      .replace(queryParameters: {
        'sort': 'highest_salary',
        'exclude_applied': '1',
        'per_page': '15',
      });

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "jobs": [
            {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant",
                "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                    "slug": "office",
                    "name": "Büro",
                    "name_localized": "Office",
                    "icon": "briefcase",
                    "color": null
                },
                "tags": [
                    "Office",
                    "Nachtschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Barrows, Christiansen and Jones",
                    "address": "586 Tremaine Row",
                    "city": "East Eliseo",
                    "postal_code": "10547-3697",
                    "lat": 46.931383,
                    "lng": 11.998284,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-08-19",
                    "shift_start_time": "01:49:00",
                    "shift_end_time": "18:57:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 17.133333333333333
                },
                "compensation": {
                    "hourly_rate_gross": 32.08,
                    "supplements": {
                        "night": 0.7,
                        "weekend": 3.86
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 5,
                    "filled": 0,
                    "available": 5
                },
                "deadlines": {
                    "application": "2026-09-05T16:18:32+00:00"
                },
                "created_at": "2026-08-05T16:18:32+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1ccb-7322-8d33-448afd9b027b",
                "title": "Testingenieur Vertretung",
                "description": "Wir suchen engagierte Testingenieur Vertretung zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f3c2-73b2-9873-51431d14404e",
                    "slug": "it_services",
                    "name": "IT Services",
                    "name_localized": "IT Services",
                    "icon": "code",
                    "color": null
                },
                "tags": [
                    "IT Services",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f842-71ba-a318-a919a24d4bcf",
                    "name": "Hotel & Spa Imperial",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "BAWAG IT-Abteilung",
                    "address": "Wiesingerstraße 4, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1030",
                    "lat": 48.2006,
                    "lng": 16.3882,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-15",
                    "end_date": "2026-09-04",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 25,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 1,
                    "filled": 0,
                    "available": 1
                },
                "deadlines": {
                    "application": "2026-08-12T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1405-709f-bca9-b0a418fb6a75",
                "title": "Silvesternacht Service",
                "description": "Wir suchen motivierte Silvesternacht Service für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Nachtschicht",
                    "HACCP Kenntnisse von Vorteil"
                ],
                "organization": {
                    "id": "019fd2b7-fe56-70d1-bb3a-641339ce636a",
                    "name": "Lagerhaus Steiermark",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Hotel Imperial Wien",
                    "address": "Kärntner Ring 16, 1015 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-02-09",
                    "end_date": "2027-02-10",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 25,
                    "supplements": {
                        "night_bonus": 50,
                        "holiday_bonus": 100,
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 12,
                    "filled": 0,
                    "available": 12
                },
                "deadlines": {
                    "application": "2027-02-04T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:26+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1cb9-72ec-bbe0-4f5f78a6a459",
                "title": "IT Helpdesk Mitarbeiter*in",
                "description": "Für unser IT-Team suchen wir erfahrene IT Helpdesk Mitarbeiter*in. Sie unterstützen bei technischen Aufgaben, Systemwartung und Kundensupport.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f3ab-70a0-9523-b474e2007212",
                    "slug": "software",
                    "name": "Software",
                    "name_localized": "Software",
                    "icon": "code",
                    "color": null
                },
                "tags": [
                    "Software",
                    "Tagschicht",
                    "IT-Grundkenntnisse",
                    "Windows/Mac Erfahrung"
                ],
                "organization": {
                    "id": "019fd2b7-fbb4-73fa-b801-e0c8a8de2651",
                    "name": "Bauwerk GmbH",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Erste Bank IT",
                    "address": "Graben 21, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-10",
                    "end_date": "2026-11-03",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 22,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 2,
                    "filled": 0,
                    "available": 2
                },
                "deadlines": {
                    "application": "2026-08-08T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1ce9-736a-9a61-22cea78283c9",
                "title": "Support-Techniker Außeneinsatz",
                "description": "Wir suchen engagierte Support-Techniker Außeneinsatz zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f3c2-73b2-9873-51431d14404e",
                    "slug": "it_services",
                    "name": "IT Services",
                    "name_localized": "IT Services",
                    "icon": "code",
                    "color": null
                },
                "tags": [
                    "IT Services",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "IT Company Graz",
                    "address": "Münzgrabenstraße 10, 8010 Graz",
                    "city": "Graz",
                    "postal_code": "8020",
                    "lat": 47.064,
                    "lng": 15.46,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-10-04",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 20,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 2,
                    "filled": 0,
                    "available": 2
                },
                "deadlines": {
                    "application": "2026-08-10T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1615-7120-9b3f-e0d90c1f57ef",
                "title": "Barkraft VIP-Lounge",
                "description": "Für unsere bevorstehende Veranstaltung suchen wir tatkräftige Unterstützung. Als Barkraft VIP-Lounge sind Sie das Aushängeschild unserer Veranstaltung und sorgen für einen reibungslosen Ablauf.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                    "slug": "event",
                    "name": "Events",
                    "name_localized": "Events",
                    "icon": "calendar-star",
                    "color": null
                },
                "tags": [
                    "Events",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-fad7-719c-887a-a0f510bbd31d",
                    "name": "IT Solutions GmbH",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Salzburger Festspiele",
                    "address": "Hofstallgasse 1, 5020 Salzburg",
                    "city": "Salzburg",
                    "postal_code": "5020",
                    "lat": 47.8095,
                    "lng": 13.055,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-10-24",
                    "end_date": "2026-11-13",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 18,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 5,
                    "filled": 0,
                    "available": 5
                },
                "deadlines": {
                    "application": "2026-10-21T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1d1e-7188-9438-b0d6050c492a",
                "title": "Kursleiter*in Deutschkurs",
                "description": "Wir suchen engagierte Kursleiter*in Deutschkurs zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f3ec-717a-ac4d-13011f3096f1",
                    "slug": "education",
                    "name": "Bildung",
                    "name_localized": "Education",
                    "icon": "graduation-cap",
                    "color": null
                },
                "tags": [
                    "Education",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b8-00e8-7186-8fa6-5b042a30b10d",
                    "name": "Transport & Spedition GmbH",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "VHS Wien",
                    "address": "Mariahilfer Straße 53, 1060 Wien",
                    "city": "Wien",
                    "postal_code": "1050",
                    "lat": 48.1944,
                    "lng": 16.3585,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-11-03",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 18,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 3,
                    "filled": 0,
                    "available": 3
                },
                "deadlines": {
                    "application": "2026-08-09T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-14dd-7377-92d2-447578550d00",
                "title": "Convention Coordinator",
                "description": "Wir suchen engagierte Convention Coordinator zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                    "slug": "hotel",
                    "name": "Hotellerie",
                    "name_localized": "Hospitality",
                    "icon": "hotel",
                    "color": null
                },
                "tags": [
                    "Hospitality",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b8-000d-719b-a73e-71665713a4fc",
                    "name": "Supermarkt AG",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Austria Center Vienna",
                    "address": "Bruno-Kreisky-Platz 1, 1220 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-25",
                    "end_date": "2026-08-30",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 18,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 5,
                    "filled": 0,
                    "available": 5
                },
                "deadlines": {
                    "application": "2026-08-23T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-17b4-72f3-9ab2-f5a5a2902d4c",
                "title": "Staplerfahrer*in Zentrallager",
                "description": "Wir suchen zuverlässige Staplerfahrer*in Zentrallager für unser Logistikzentrum. Sie unterstützen uns bei der Warenwirtschaft, Kommissionierung und dem allgemeinen Lagerbetrieb. Körperliche Belastbarkeit und Teamfähigkeit sind Voraussetzung.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f38e-7255-bfe3-0f010aca0086",
                    "slug": "logistics",
                    "name": "Logistik",
                    "name_localized": "Logistics",
                    "icon": "truck",
                    "color": null
                },
                "tags": [
                    "Logistics",
                    "Tagschicht",
                    "Körperliche Belastbarkeit",
                    "Deutschkenntnisse Grundstufe"
                ],
                "organization": {
                    "id": "019fd2b7-f68e-713d-888b-ff71714f0874",
                    "name": "Logistik Austria GmbH",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "REWE Lager Linz",
                    "address": "Industriestraße 3, 4030 Linz",
                    "city": "Linz",
                    "postal_code": "4030",
                    "lat": 48.2757,
                    "lng": 14.3213,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2027-02-01",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16.5,
                    "supplements": {
                        "weekend_bonus": 8,
                        "forklift_license": true
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 4,
                    "filled": 0,
                    "available": 4
                },
                "deadlines": {
                    "application": "2026-08-07T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1d63-71f8-b82b-eb790b71f0b3",
                "title": "Produktionshelfer*in Fabrik",
                "description": "Wir suchen engagierte Produktionshelfer*in Fabrik zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f388-7098-b463-ae170338e60f",
                    "slug": "production",
                    "name": "Produktion",
                    "name_localized": "Production",
                    "icon": "industry",
                    "color": null
                },
                "tags": [
                    "Production",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f9fd-71a9-b71e-466dc30c8aac",
                    "name": "Pflege & Betreuung GmbH",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "voestalpine Stahl GmbH",
                    "address": "voestalpine-Straße 1, 4020 Linz",
                    "city": "Linz",
                    "postal_code": "4030",
                    "lat": 48.2757,
                    "lng": 14.3213,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-10",
                    "end_date": "2026-11-03",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16.5,
                    "supplements": {
                        "weekend_bonus": 8,
                        "shift_allowance": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 10,
                    "filled": 0,
                    "available": 10
                },
                "deadlines": {
                    "application": "2026-08-07T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-15d2-72e5-a3dc-cf69f63524e6",
                "title": "Technik Aufbau Konzert",
                "description": "Für unsere bevorstehende Veranstaltung suchen wir tatkräftige Unterstützung. Als Technik Aufbau Konzert sind Sie das Aushängeschild unserer Veranstaltung und sorgen für einen reibungslosen Ablauf.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                    "slug": "event",
                    "name": "Events",
                    "name_localized": "Events",
                    "icon": "calendar-star",
                    "color": null
                },
                "tags": [
                    "Events",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Wiener Stadthalle",
                    "address": "Roland-Rainer-Platz 1, 1150 Wien",
                    "city": "Wien",
                    "postal_code": "1030",
                    "lat": 48.1883,
                    "lng": 16.3957,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-10",
                    "end_date": "2026-08-11",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 8,
                    "filled": 0,
                    "available": 8
                },
                "deadlines": {
                    "application": "2026-08-07T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-171e-7183-b2d6-b7fae57dd92b",
                "title": "Personenkontrolle Flughafen",
                "description": "Als Personenkontrolle Flughafen sind Sie für die Sicherheit unserer Einrichtung/Veranstaltung verantwortlich. Sie überwachen Zugänge, kontrollieren Personen und sorgen für Ordnung und Sicherheit.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f39c-7064-8f21-15054bddd646",
                    "slug": "security",
                    "name": "Security",
                    "name_localized": "Security",
                    "icon": "shield",
                    "color": null
                },
                "tags": [
                    "Security",
                    "Tagschicht",
                    "Sicherheitsausweis (§ 133 GewO)",
                    "Erste-Hilfe-Kurs"
                ],
                "organization": {
                    "id": "019fd2b8-000d-719b-a73e-71665713a4fc",
                    "name": "Supermarkt AG",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Flughafen Wien-Schwechat",
                    "address": "Flughafen Wien, 1300 Wien",
                    "city": "Wien",
                    "postal_code": "1300",
                    "lat": 48.1103,
                    "lng": 16.5697,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-10",
                    "end_date": "2026-11-03",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": {
                        "weekend_bonus": 8,
                        "shift_allowance": 10
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 6,
                    "filled": 0,
                    "available": 6
                },
                "deadlines": {
                    "application": "2026-08-06T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1c02-7040-a155-5d35418e0953",
                "title": "Medizinische Sekretärin",
                "description": "Wir suchen engagierte Medizinische Sekretärin zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f3d8-72d5-842e-c1f00337e9ec",
                    "slug": "healthcare",
                    "name": "Gesundheitswesen",
                    "name_localized": "Healthcare",
                    "icon": "heart-pulse",
                    "color": null
                },
                "tags": [
                    "Healthcare",
                    "Tagschicht",
                    "Medizinische Grundkenntnisse",
                    "Hygieneschulung"
                ],
                "organization": {
                    "id": "019fd2b7-fca6-71cf-8683-c97f6f50a24a",
                    "name": "Reinigungsservice Alpin",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Ambulanz AKH Wien",
                    "address": "Währinger Gürtel 18-20, 1090 Wien",
                    "city": "Wien",
                    "postal_code": "1090",
                    "lat": 48.2228,
                    "lng": 16.3568,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-10-04",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 2,
                    "filled": 0,
                    "available": 2
                },
                "deadlines": {
                    "application": "2026-08-07T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:28+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1547-703f-bfe4-177b0923bd1d",
                "title": "Concierge Nightshift",
                "description": "Wir suchen engagierte Concierge Nightshift zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                    "slug": "hotel",
                    "name": "Hotellerie",
                    "name_localized": "Hospitality",
                    "icon": "hotel",
                    "color": null
                },
                "tags": [
                    "Hospitality",
                    "Nachtschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Grand Hotel Wien",
                    "address": "Kärntner Ring 9, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-20",
                    "end_date": "2027-08-05",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": {
                        "night_bonus": 20,
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 1,
                    "filled": 0,
                    "available": 1
                },
                "deadlines": {
                    "application": "2026-08-17T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1287-7146-a673-6798f3d03acc",
                "title": "Barkeeper Spätschicht Club",
                "description": "Wir suchen motivierte Barkeeper Spätschicht Club für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Nachtschicht",
                    "HACCP Kenntnisse von Vorteil"
                ],
                "organization": {
                    "id": "019fd2b7-f5ae-7390-a778-c08c8d56cdc0",
                    "name": "Event Solutions Austria",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Club Passage",
                    "address": "Babenbergerstraße 9, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1070",
                    "lat": 48.2003,
                    "lng": 16.3465,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-08",
                    "end_date": "2026-11-03",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": {
                        "night_bonus": 15,
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 4,
                    "filled": 0,
                    "available": 4
                },
                "deadlines": {
                    "application": "2026-08-06T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:26+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 5,
            "per_page": 15,
            "total": 71
        }
    }
}
 

Request      

GET api/v1/mobile/jobs

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

category   string  optional    

Filter by job category slug, e.g. gastro.

categories   string[]  optional    

Several category slugs, comma separated, e.g. gastro,hotel.

state   string  optional    

Austrian state, e.g. wien.

city   string  optional    

City name, e.g. Wien.

lat   number  optional    

Latitude for a radius search; send with lng and radius.

lng   number  optional    

Longitude for a radius search.

radius   integer  optional    

Radius in km around lat/lng.

min_rate   number  optional    

Minimum gross hourly rate.

max_rate   number  optional    

Maximum gross hourly rate.

shift_type   string  optional    

One of day, night, evening.

start_date   string  optional    

date Only jobs starting on or after this date.

end_date   string  optional    

date Only jobs starting on or before this date.

sort   string  optional    

Sort option. Example: highest_salary

search   string  optional    

Free text over title and description.

exclude_applied   boolean  optional    

Exclude already applied jobs (default true). Example: true

per_page   integer  optional    

Items per page (max 50). Example: 15

Get Job Filters

Returns all available filter options for the job listing screen. This endpoint is designed to be called once per session (on app start or when opening the filter sheet) and cached client-side.

Includes live job counts per category and the actual hourly-rate range from currently active jobs so the Flutter slider is always bounded correctly.

Response is cacheable for 1 hour (Cache-Control: public, max-age=3600).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/jobs/filters" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/filters"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/filters';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/filters');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "categories": [
            {
                "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                "slug": "gastro",
                "name": "Gastronomie",
                "name_localized": "Gastronomy",
                "icon": "utensils",
                "is_featured": true,
                "parent_id": null,
                "job_count": 13
            },
            {
                "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                "slug": "hotel",
                "name": "Hotellerie",
                "name_localized": "Hospitality",
                "icon": "hotel",
                "is_featured": true,
                "parent_id": null,
                "job_count": 5
            },
            {
                "id": "019fd2b7-f377-7108-8400-efa89715a39f",
                "slug": "food_service",
                "name": "Food Service",
                "name_localized": "Food Service",
                "icon": "utensils",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f37a-72a9-84a9-7862f636e2fd",
                "slug": "catering",
                "name": "Catering",
                "name_localized": "Catering",
                "icon": "utensils",
                "is_featured": false,
                "parent_id": null,
                "job_count": 4
            },
            {
                "id": "019fd2b7-f37d-712d-92b3-aa5540f6b2a7",
                "slug": "retail",
                "name": "Einzelhandel",
                "name_localized": "Retail",
                "icon": "shopping-cart",
                "is_featured": false,
                "parent_id": null,
                "job_count": 4
            },
            {
                "id": "019fd2b7-f380-7300-91c6-75c968e6d045",
                "slug": "supermarket",
                "name": "Lebensmittelhandel",
                "name_localized": "Grocery",
                "icon": "shopping-cart",
                "is_featured": false,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f384-7071-b096-674d16b4ad00",
                "slug": "pharmacy",
                "name": "Apotheke",
                "name_localized": "Pharmacy",
                "icon": "heart-pulse",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f388-7098-b463-ae170338e60f",
                "slug": "production",
                "name": "Produktion",
                "name_localized": "Production",
                "icon": "industry",
                "is_featured": false,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f38b-7209-8b83-b345b172def2",
                "slug": "warehouse",
                "name": "Lager",
                "name_localized": "Warehouse",
                "icon": "industry",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f38e-7255-bfe3-0f010aca0086",
                "slug": "logistics",
                "name": "Logistik",
                "name_localized": "Logistics",
                "icon": "truck",
                "is_featured": true,
                "parent_id": null,
                "job_count": 5
            },
            {
                "id": "019fd2b7-f391-727d-9871-61207ae27ab3",
                "slug": "manufacturing",
                "name": "Fertigung",
                "name_localized": "Manufacturing",
                "icon": "industry",
                "is_featured": false,
                "parent_id": null,
                "job_count": 1
            },
            {
                "id": "019fd2b7-f394-7015-b80e-5fa954b27658",
                "slug": "transportation",
                "name": "Transport",
                "name_localized": "Transportation",
                "icon": "truck",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                "slug": "event",
                "name": "Events",
                "name_localized": "Events",
                "icon": "calendar-star",
                "is_featured": false,
                "parent_id": null,
                "job_count": 13
            },
            {
                "id": "019fd2b7-f39c-7064-8f21-15054bddd646",
                "slug": "security",
                "name": "Security",
                "name_localized": "Security",
                "icon": "shield",
                "is_featured": false,
                "parent_id": null,
                "job_count": 5
            },
            {
                "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                "slug": "office",
                "name": "Büro",
                "name_localized": "Office",
                "icon": "briefcase",
                "is_featured": false,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f3a2-7147-883f-c3207db81b51",
                "slug": "reception",
                "name": "Empfang",
                "name_localized": "Reception",
                "icon": "briefcase",
                "is_featured": false,
                "parent_id": null,
                "job_count": 1
            },
            {
                "id": "019fd2b7-f3a5-70a8-a3b1-9140f01abf8e",
                "slug": "call_center",
                "name": "Call Center",
                "name_localized": "Call Center",
                "icon": "briefcase",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3a8-72f6-8ad1-0f499f6816e1",
                "slug": "customer_service",
                "name": "Kundenservice",
                "name_localized": "Customer Service",
                "icon": "briefcase",
                "is_featured": false,
                "parent_id": null,
                "job_count": 1
            },
            {
                "id": "019fd2b7-f3ab-70a0-9523-b474e2007212",
                "slug": "software",
                "name": "Software",
                "name_localized": "Software",
                "icon": "code",
                "is_featured": true,
                "parent_id": null,
                "job_count": 1
            },
            {
                "id": "019fd2b7-f3c2-73b2-9873-51431d14404e",
                "slug": "it_services",
                "name": "IT Services",
                "name_localized": "IT Services",
                "icon": "code",
                "is_featured": false,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f3cc-728f-a1eb-11d5120b3dca",
                "slug": "cleaning",
                "name": "Reinigung",
                "name_localized": "Cleaning",
                "icon": "broom",
                "is_featured": false,
                "parent_id": null,
                "job_count": 3
            },
            {
                "id": "019fd2b7-f3d1-72e1-a261-10207b6d759d",
                "slug": "facility",
                "name": "Facility Management",
                "name_localized": "Facility Management",
                "icon": "broom",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3d5-71b4-bece-4ea026852495",
                "slug": "care",
                "name": "Pflege/Betreuung",
                "name_localized": "Care",
                "icon": "heart-pulse",
                "is_featured": false,
                "parent_id": null,
                "job_count": 1
            },
            {
                "id": "019fd2b7-f3d8-72d5-842e-c1f00337e9ec",
                "slug": "healthcare",
                "name": "Gesundheitswesen",
                "name_localized": "Healthcare",
                "icon": "heart-pulse",
                "is_featured": false,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f3dc-70cb-a662-0fc23b0ec307",
                "slug": "construction",
                "name": "Bau (Hilfstätigkeiten)",
                "name_localized": "Construction",
                "icon": "building",
                "is_featured": false,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f3df-70bb-8881-36c5f2234495",
                "slug": "real_estate",
                "name": "Immobilien",
                "name_localized": "Real Estate",
                "icon": "building",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3e5-714f-8db2-a5ae4350ae5c",
                "slug": "graphic_design",
                "name": "Grafikdesign",
                "name_localized": "Graphic Design",
                "icon": "palette",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3e9-71c6-b382-7451adaf4bb7",
                "slug": "media",
                "name": "Medien",
                "name_localized": "Media",
                "icon": "palette",
                "is_featured": true,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3ec-717a-ac4d-13011f3096f1",
                "slug": "education",
                "name": "Bildung",
                "name_localized": "Education",
                "icon": "graduation-cap",
                "is_featured": true,
                "parent_id": null,
                "job_count": 2
            },
            {
                "id": "019fd2b7-f3f0-70a8-b7be-6be4c17c9429",
                "slug": "finance",
                "name": "Finanzen",
                "name_localized": "Finance",
                "icon": "chart-line",
                "is_featured": true,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3f3-737d-9abe-5edb92ceb02a",
                "slug": "agriculture",
                "name": "Landwirtschaft",
                "name_localized": "Agriculture",
                "icon": "leaf",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            },
            {
                "id": "019fd2b7-f3f6-70c7-868b-6ac419f0d02c",
                "slug": "other",
                "name": "Sonstiges",
                "name_localized": "Other",
                "icon": "ellipsis",
                "is_featured": false,
                "parent_id": null,
                "job_count": 0
            }
        ],
        "categories_grouped": {
            "Gastronomie": [],
            "Hotellerie": [],
            "Food Service": [],
            "Catering": [],
            "Einzelhandel": [],
            "Lebensmittelhandel": [],
            "Apotheke": [],
            "Produktion": [],
            "Lager": [],
            "Logistik": [],
            "Fertigung": [],
            "Transport": [],
            "Events": [],
            "Security": [],
            "Büro": [],
            "Empfang": [],
            "Call Center": [],
            "Kundenservice": [],
            "Software": [],
            "IT Services": [],
            "Reinigung": [],
            "Facility Management": [],
            "Pflege/Betreuung": [],
            "Gesundheitswesen": [],
            "Bau (Hilfstätigkeiten)": [],
            "Immobilien": [],
            "Grafikdesign": [],
            "Medien": [],
            "Bildung": [],
            "Finanzen": [],
            "Landwirtschaft": [],
            "Sonstiges": []
        },
        "states": [
            {
                "value": "wien",
                "label": "Wien"
            },
            {
                "value": "niederoesterreich",
                "label": "Niederösterreich"
            },
            {
                "value": "oberoesterreich",
                "label": "Oberösterreich"
            },
            {
                "value": "steiermark",
                "label": "Steiermark"
            },
            {
                "value": "salzburg",
                "label": "Salzburg"
            },
            {
                "value": "tirol",
                "label": "Tirol"
            },
            {
                "value": "vorarlberg",
                "label": "Vorarlberg"
            },
            {
                "value": "kaernten",
                "label": "Kärnten"
            },
            {
                "value": "burgenland",
                "label": "Burgenland"
            }
        ],
        "sort_options": [
            {
                "value": "newest",
                "label": "Neueste"
            },
            {
                "value": "highest_salary",
                "label": "Höchstes Gehalt"
            },
            {
                "value": "lowest_salary",
                "label": "Niedrigstes Gehalt"
            },
            {
                "value": "nearest",
                "label": "Nächstgelegen"
            },
            {
                "value": "starting_soon",
                "label": "Bald startend"
            },
            {
                "value": "morning_shifts",
                "label": "Morgenschichten"
            },
            {
                "value": "night_shifts",
                "label": "Nachtschichten"
            },
            {
                "value": "featured",
                "label": "Empfohlen"
            },
            {
                "value": "relevance",
                "label": "Relevanz"
            }
        ],
        "shift_types": [
            {
                "value": "day",
                "label": "Tagschicht"
            },
            {
                "value": "night",
                "label": "Nachtschicht"
            },
            {
                "value": "weekend",
                "label": "Wochenendschicht"
            },
            {
                "value": "holiday",
                "label": "Feiertagsschicht"
            }
        ],
        "rate_range": {
            "min": 10,
            "max": 100,
            "step": 0.5,
            "active_min": 13,
            "active_max": 32.08
        }
    }
}
 

Request      

GET api/v1/mobile/jobs/filters

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Get detailed information about a specific job.

Returns comprehensive job data including shifts, requirements, organization info, and application statistics.

Two shift arrays come back. They are keyed identically — date, formatted_date, start_time, end_time, available_spots, is_past, is_applied — so one parser reads both. What differs is whether the entries are real:

Where they disagree, shift_slots is right: a company can move one shift's hours or headcount, and the calendar will not know.

applied_shift_dates is output only: the dates this worker already chose. shift_slots[].is_applied says the same thing per shift.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "title": "Accountant",
            "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
            "status": "active",
            "is_featured": false,
            "category": {
                "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                "slug": "office",
                "name": "Büro",
                "name_localized": "Office",
                "icon": "briefcase",
                "color": null,
                "kollektivvertrag": "Handel/Büro",
                "description": null,
                "is_featured": false
            },
            "tags": [
                "Office",
                "Nachtschicht"
            ],
            "organization": {
                "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                "name": "Caritas",
                "logo_url": null,
                "description": "Caritas test organisation used for end-to-end portal QA. Safe to delete.",
                "website": "https://www.caritas.at",
                "city": "Vienna",
                "country": "Austria",
                "industry_code": null,
                "company_size": null,
                "is_verified": true
            },
            "location": {
                "name": "Barrows, Christiansen and Jones",
                "address": "586 Tremaine Row",
                "city": "East Eliseo",
                "postal_code": "10547-3697",
                "lat": 46.931383,
                "lng": 11.998284,
                "distance_km": null
            },
            "schedule": {
                "start_date": "2026-08-12",
                "end_date": "2026-08-19",
                "shift_start_time": "01:49:00",
                "shift_end_time": "18:57:00",
                "shift_type": "night",
                "shift_type_label": "Nachtschicht",
                "duration_hours": 17.133333333333333
            },
            "compensation": {
                "hourly_rate_gross": 32.08,
                "supplements": {
                    "night": 0.7,
                    "weekend": 3.86
                },
                "estimated_total_gross": null,
                "currency": "EUR"
            },
            "vacancies": {
                "total": 5,
                "filled": 0,
                "available": 5,
                "is_fully_booked": false,
                "is_open_for_applications": true
            },
            "deadlines": {
                "application": "2026-09-05T16:18:32+00:00",
                "confirmation": "2026-08-16T10:20:06+00:00"
            },
            "created_at": "2026-08-05T16:18:32+00:00",
            "is_saved": false,
            "can_apply": true,
            "rate": null,
            "published_at": null,
            "shifts": [
                {
                    "date": "2026-08-12",
                    "formatted_date": "Mi., 12. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-13",
                    "formatted_date": "Do., 13. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-14",
                    "formatted_date": "Fr., 14. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-15",
                    "formatted_date": "Sa., 15. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-16",
                    "formatted_date": "So., 16. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-17",
                    "formatted_date": "Mo., 17. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-18",
                    "formatted_date": "Di., 18. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                },
                {
                    "date": "2026-08-19",
                    "formatted_date": "Mi., 19. August",
                    "start_time": "01:49:00",
                    "end_time": "18:57:00",
                    "available_spots": 5,
                    "is_past": false,
                    "is_applied": false
                }
            ],
            "shift_slots": [
                {
                    "id": "019fd2b8-28d5-7103-b951-9f6ff1d94556",
                    "date": "2026-08-05",
                    "formatted_date": "Mi., 5. August",
                    "start_time": "08:00:00",
                    "end_time": "16:00:00",
                    "available_spots": 2,
                    "is_past": false,
                    "is_applied": false,
                    "workers_needed": 2,
                    "is_full": false,
                    "meeting_point": null,
                    "application_deadline": null
                }
            ],
            "application_id": null,
            "application_status": null,
            "applied_shift_dates": [],
            "applications": {
                "total": 1,
                "pending": 0
            },
            "requirements": [
                {
                    "id": "019fd2b8-28c9-704f-b685-1cf9be0c3dff",
                    "type": "document",
                    "type_label": "Dokument",
                    "name": "Reisepass",
                    "description": null,
                    "is_mandatory": true
                },
                {
                    "id": "019fd2b8-28cb-7212-a83d-b62d519bbbf1",
                    "type": "document",
                    "type_label": "Dokument",
                    "name": "Führerschein",
                    "description": null,
                    "is_mandatory": true
                }
            ],
            "custom_document_requests": [],
            "qualifications": [],
            "dress_code": "Tempore necessitatibus quia illo suscipit.",
            "equipment_provided": "Adipisci fugiat doloremque atque consectetur necessitatibus sunt cumque.",
            "rules": {
                "payment_terms": "Direktzahlung (innerhalb von 3 Werktagen)",
                "cancellation_policy": "Stornierungsrichtlinien gelten",
                "insurance": "Kostenlose Unfallversicherung inklusive"
            },
            "contact": {
                "person_avatar_url": null
            },
            "additional_info": {
                "parking_info": null,
                "special_instructions": null
            }
        }
    }
}
 

Example response (404, Not Found):


{
    "status": "RESOURCE_NOT_FOUND",
    "message": "Job not found"
}
 

Request      

GET api/v1/mobile/jobs/{jobId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

The job UUID. Example: 9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f

Get Job Requirements

requires authentication

What the authenticated employee needs before they can apply for a job: every document-backed requirement the company set, whether the employee currently satisfies it, and when the satisfying document expires.

The same rules gate the application itself — {@see JobRequirementGate} is shared with ApplyForJobAction — so this can never disagree with what happens on submit. Without it the app could only discover an unmet requirement by attempting to apply and reading back the rejection.

ready reports on mandatory requirements only. Optional ones are returned so the employee can see them, but they never block an application.

Item status is one of verified, pending, rejected, reupload_requested, expired or not_provided.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2

The framework agreement is reported separately from `items`: everything
in that list is answered by uploading a document, while this one is
answered by signing. Its id is included so the app can post straight to
`POST /mobile/contracts/1/sign` and return here. It counts
towards `ready` all the same — applying is refused without it.

`account_status` is the worker&#039;s lifecycle state, in the same vocabulary
as the 403 notices. Applying and signing both need `active`; until then
`ready` is false however complete the checklist is, and the agreement
reads `awaiting_approval` rather than `signature_required` — signing
before approval answers 403, so an app that offered the button would be
offering a dead one./requirements" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2

The framework agreement is reported separately from `items`: everything
in that list is answered by uploading a document, while this one is
answered by signing. Its id is included so the app can post straight to
`POST /mobile/contracts/1/sign` and return here. It counts
towards `ready` all the same — applying is refused without it.

`account_status` is the worker&#039;s lifecycle state, in the same vocabulary
as the 403 notices. Applying and signing both need `active`; until then
`ready` is false however complete the checklist is, and the agreement
reads `awaiting_approval` rather than `signature_required` — signing
before approval answers 403, so an app that offered the button would be
offering a dead one./requirements"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2

The framework agreement is reported separately from `items`: everything
in that list is answered by uploading a document, while this one is
answered by signing. Its id is included so the app can post straight to
`POST /mobile/contracts/1/sign` and return here. It counts
towards `ready` all the same — applying is refused without it.

`account_status` is the worker's lifecycle state, in the same vocabulary
as the 403 notices. Applying and signing both need `active`; until then
`ready` is false however complete the checklist is, and the agreement
reads `awaiting_approval` rather than `signature_required` — signing
before approval answers 403, so an app that offered the button would be
offering a dead one./requirements';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2

The framework agreement is reported separately from `items`: everything
in that list is answered by uploading a document, while this one is
answered by signing. Its id is included so the app can post straight to
`POST /mobile/contracts/1/sign` and return here. It counts
towards `ready` all the same — applying is refused without it.

`account_status` is the worker&#039;s lifecycle state, in the same vocabulary
as the 403 notices. Applying and signing both need `active`; until then
`ready` is false however complete the checklist is, and the agreement
reads `awaiting_approval` rather than `signature_required` — signing
before approval answers 403, so an app that offered the button would be
offering a dead one./requirements');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Ready to apply):


{
    "status": "SUCCESS",
    "message": "Requirements retrieved.",
    "data": {
        "ready": true,
        "account_status": "active",
        "contract": {
            "status": "signed",
            "id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
        },
        "items": []
    }
}
 

Example response (200, Still under review):


{
    "status": "SUCCESS",
    "message": "Requirements retrieved.",
    "data": {
        "ready": false,
        "account_status": "pending_approval",
        "contract": {
            "status": "awaiting_approval",
            "id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
        },
        "items": []
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Requirements retrieved.",
    "data": {
        "ready": false,
        "account_status": "active",
        "contract": {
            "status": "unavailable",
            "id": null
        },
        "items": [
            {
                "requirement_code": "passport",
                "name": "Reisepass",
                "description": null,
                "requirement_type": "document",
                "requirement_type_label": "Dokument",
                "is_mandatory": true,
                "accepts": [
                    "passport"
                ],
                "satisfied": true,
                "status": "verified",
                "valid_until": "2033-05-22"
            },
            {
                "requirement_code": "driver_license",
                "name": "Führerschein",
                "description": null,
                "requirement_type": "document",
                "requirement_type_label": "Dokument",
                "is_mandatory": true,
                "accepts": [
                    "driver_license"
                ],
                "satisfied": false,
                "status": "not_provided",
                "valid_until": null
            }
        ]
    }
}
 

Example response (404, Job not found):


{
    "status": "RESOURCE_NOT_FOUND",
    "message": "The job was not found."
}
 

Request      

GET api/v1/mobile/jobs/{jobId}/requirements

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

The job to check. Example: `019f9939-c4c3-70fb-a54e-5ebf63db36d2

The framework agreement is reported separately from items: everything in that list is answered by uploading a document, while this one is answered by signing. Its id is included so the app can post straight to POST /mobile/contracts/{contractId}/sign and return here. It counts towards ready all the same — applying is refused without it.

account_status is the worker's lifecycle state, in the same vocabulary as the 403 notices. Applying and signing both need active; until then ready is false however complete the checklist is, and the agreement reads awaiting_approval rather than signature_required — signing before approval answers 403, so an app that offered the button would be offering a dead one.`

Apply For Job

requires authentication

Applies to a job and, in the same request, says which of its shifts the worker wants. The application is job-level; the chosen shifts are attached to it.

shift_ids come from shift_slots in GET /mobile/jobs/{jobId} — the id of each slot the worker picked. Do not send dates: the shifts array on that same response is a display-only calendar synthesised from the job's date range and carries no ids, and applied_shift_dates is what the platform reports back after applying, not an input. A job whose shift_slots is empty has no shifts to choose and cannot be applied to.

A full shift may still be chosen — that places the worker on standby for it rather than refusing the application.

Calling this again for a job already applied to adds shifts rather than being refused: a worker applies for shifts, and wanting a further day is a new request, not a repeat. The application keeps its id and status, so the company still sees one applicant, and the response carries added_shift_ids — the subset that was new — with 200 instead of 201. On a selected application the added shifts are booked immediately — the company already chose this worker, so the new days become assignments (with their own Überlassungsvertrag) right away; if none of them still has room, the request is refused and nothing is attached.

It is refused with DUPLICATE_ENTRY only when every shift sent is already on the application, or when it is closed (rejected, withdrawn, expired, cancelled): there the decision was against working, and adding work to it is not the worker's to do.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/apply" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"shift_ids\": [
        \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
    ],
    \"acknowledge_conflict\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/apply"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "shift_ids": [
        "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
    ],
    "acknowledge_conflict": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/apply';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'shift_ids' => ['019f9939-c4c3-70fb-a54e-5ebf63db36d2'],
            'acknowledge_conflict' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/apply');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "shift_ids": [
        "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
    ],
    "acknowledge_conflict": false
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Further shifts added to an existing application):


{
    "status": "SUCCESS",
    "message": "Die Schichten wurden Ihrer Bewerbung hinzugefügt.",
    "data": {
        "application": {
            "id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
            "status": "pending",
            "added_shift_ids": [
                "019f9939-c4c3-70fb-a54e-5ebf63db36d3"
            ],
            "shift_dates": [
                "2026-08-12",
                "2026-08-19"
            ]
        }
    }
}
 

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your application has been submitted successfully.",
    "data": {
        "application": {
            "id": "019fd2b8-76c6-72bb-a2d9-98ed4934fb02",
            "job_id": "019fd2b8-7568-72f7-89ad-5bec0d9779ff",
            "status": "pending",
            "applied_at": "2026-08-05T16:18:52+00:00",
            "has_scheduling_conflict": false,
            "shift_ids": [
                "019fd2b8-756e-70aa-a7ad-01157af79df0"
            ],
            "shift_dates": [
                "2026-08-12"
            ]
        }
    }
}
 

Example response (409, Nothing new in the request):


{
    "status": "DUPLICATE_ENTRY",
    "message": "Sie haben sich für diese Schichten bereits beworben."
}
 

Example response (422, No shifts chosen):


{
    "status": "VALIDATION_ERROR",
    "message": "Bitte wähle mindestens eine Schicht aus, bevor du dich bewirbst."
}
 

Example response (422, A chosen shift is not selectable):


{
    "status": "VALIDATION_ERROR",
    "message": "Mindestens eine der gewählten Schichten ist nicht mehr verfügbar."
}
 

Request      

POST api/v1/mobile/jobs/{jobId}/apply

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

shift_ids   string[]     

The shifts applied for, as shift_slots[].id values from the job detail. At least one; each must belong to this job, not be cancelled, and its application deadline must not have passed.

acknowledge_conflict   boolean  optional    

Apply anyway when the chosen shifts clash with work the worker already has. Example: false

documents   object  optional    

Files this job asks applicants to attach, keyed by the id from custom_document_requests — sent as multipart, e.g. documents[019f9939-…].

List employee's saved jobs (Merkzettel).

requires authentication

Returns only active saved jobs (not auto-removed). Includes job details and application status if applied.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/saved-jobs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/saved-jobs"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/saved-jobs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/saved-jobs');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "saved_jobs": [],
        "meta": {
            "total": 0
        }
    }
}
 

Request      

GET api/v1/mobile/saved-jobs

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Save Job

requires authentication

Puts a job on the worker's Merkzettel, with an optional note and reminder preferences. Saving a job that is already saved updates it rather than adding a second entry, so this doubles as the edit call: send only what is changing, omitted fields keep their value.

DELETE on the same path unsaves it.

POST /mobile/jobs/{jobId}/bookmark also exists — it toggles saved state and takes no body. Use that for a one-tap heart, and this when the worker is setting a note or a reminder.

notify_deadline is acted on: a worker who asks for it is told once, shortly before applications close, by the jobs:remind-saved-deadlines sweep.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"notes\": \"Nachtschicht, Anfahrt 25 min\",
    \"notify_deadline\": true,
    \"notify_vacancy_change\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "notes": "Nachtschicht, Anfahrt 25 min",
    "notify_deadline": true,
    "notify_vacancy_change": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'notes' => 'Nachtschicht, Anfahrt 25 min',
            'notify_deadline' => true,
            'notify_vacancy_change' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "notes": "Nachtschicht, Anfahrt 25 min",
    "notify_deadline": true,
    "notify_vacancy_change": false
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been saved.",
    "data": {
        "saved_job_id": "019fd2b8-7687-717b-a68d-76c060caa63f",
        "saved_at": "2026-08-05T16:18:52+00:00"
    }
}
 

Request      

POST api/v1/mobile/jobs/{jobId}/save

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

notes   string  optional    

The worker's own note, shown back to them on the saved list. Example: Nachtschicht, Anfahrt 25 min

notify_deadline   boolean  optional    

Remind the worker before applications close. Defaults to true on a first save. Example: true

notify_vacancy_change   boolean  optional    

Tell the worker when the number of open spots changes. Example: false

Save Job

requires authentication

Puts a job on the worker's Merkzettel, with an optional note and reminder preferences. Saving a job that is already saved updates it rather than adding a second entry, so this doubles as the edit call: send only what is changing, omitted fields keep their value.

DELETE on the same path unsaves it.

POST /mobile/jobs/{jobId}/bookmark also exists — it toggles saved state and takes no body. Use that for a one-tap heart, and this when the worker is setting a note or a reminder.

notify_deadline is acted on: a worker who asks for it is told once, shortly before applications close, by the jobs:remind-saved-deadlines sweep.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"notes\": \"Nachtschicht, Anfahrt 25 min\",
    \"notify_deadline\": true,
    \"notify_vacancy_change\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "notes": "Nachtschicht, Anfahrt 25 min",
    "notify_deadline": true,
    "notify_vacancy_change": false
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'notes' => 'Nachtschicht, Anfahrt 25 min',
            'notify_deadline' => true,
            'notify_vacancy_change' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/save');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "notes": "Nachtschicht, Anfahrt 25 min",
    "notify_deadline": true,
    "notify_vacancy_change": false
};

  final response = await http.delete(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been saved.",
    "data": {
        "saved_job_id": "019fd2b8-7687-717b-a68d-76c060caa63f",
        "saved_at": "2026-08-05T16:18:52+00:00"
    }
}
 

Request      

DELETE api/v1/mobile/jobs/{jobId}/save

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

notes   string  optional    

The worker's own note, shown back to them on the saved list. Example: Nachtschicht, Anfahrt 25 min

notify_deadline   boolean  optional    

Remind the worker before applications close. Defaults to true on a first save. Example: true

notify_vacancy_change   boolean  optional    

Tell the worker when the number of open spots changes. Example: false

Toggle bookmark (save/unsave) for a job.

requires authentication

POST /mobile/jobs/{jobId}/bookmark

If the job is already bookmarked → removes it (is_bookmarked: false). If not bookmarked → saves it (is_bookmarked: true).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f/bookmark" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f/bookmark"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f/bookmark';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/jobs/9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f/bookmark');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Unbookmarked):


{
    "status": "SUCCESS",
    "data": {
        "is_bookmarked": false,
        "saved_job_id": null
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "is_bookmarked": true,
        "saved_job_id": "019fd2b8-3879-73c5-8283-53557bc9c066"
    }
}
 

Example response (404, Not Found):


{
    "status": "RESOURCE_NOT_FOUND",
    "message": "Job nicht gefunden."
}
 

Request      

POST api/v1/mobile/jobs/{jobId}/bookmark

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

The job UUID. Example: 9c7f8e6d-5b4a-3c2d-1e0f-9a8b7c6d5e4f

requires authentication

Returns jobs scored by:

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/recommended-jobs?limit=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/recommended-jobs"
);

const params = {
    "limit": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/recommended-jobs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'limit' => '20',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/recommended-jobs')
      .replace(queryParameters: {
        'limit': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "recommendations": [
            {
                "job": {
                    "id": "019fd2b8-1a90-7125-af02-72f37fef65ea",
                    "title": "Backoffice Assistenz",
                    "description": "Wir suchen engagierte Backoffice Assistenz zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                        "slug": "office",
                        "name": "Büro",
                        "name_localized": "Office",
                        "icon": "briefcase",
                        "color": null
                    },
                    "tags": [
                        "Office",
                        "Tagschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-f768-7325-b254-280a67b1a995",
                        "name": "Retail Services GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Steuerberatung Wien",
                        "address": "Margaretenstraße 90, 1050 Wien",
                        "city": "Wien",
                        "postal_code": "1050",
                        "lat": 48.1944,
                        "lng": 16.3585,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-15",
                        "end_date": "2026-09-04",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 16,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 1,
                        "filled": 0,
                        "available": 1
                    },
                    "deadlines": {
                        "application": "2026-08-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:28+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 61.5,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 10,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1265-737c-ae21-e5265dc709d8",
                    "title": "Servicekraft Restaurant Mitte",
                    "description": "Wir suchen motivierte Servicekraft Restaurant Mitte für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-ff32-7117-ab28-06e2d3082002",
                        "name": "Bildungszentrum Mitte",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Brasserie Mitte",
                        "address": "Schwarzenbergplatz 3, 1010 Wien",
                        "city": "Wien",
                        "postal_code": "1010",
                        "lat": 48.2082,
                        "lng": 16.3739,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-10",
                        "end_date": "2026-09-09",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 8,
                        "filled": 0,
                        "available": 8
                    },
                    "deadlines": {
                        "application": "2026-08-07T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1287-7146-a673-6798f3d03acc",
                    "title": "Barkeeper Spätschicht Club",
                    "description": "Wir suchen motivierte Barkeeper Spätschicht Club für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Nachtschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-f5ae-7390-a778-c08c8d56cdc0",
                        "name": "Event Solutions Austria",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Club Passage",
                        "address": "Babenbergerstraße 9, 1010 Wien",
                        "city": "Wien",
                        "postal_code": "1070",
                        "lat": 48.2003,
                        "lng": 16.3465,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-08",
                        "end_date": "2026-11-03",
                        "shift_start_time": "22:00:00",
                        "shift_end_time": "06:00:00",
                        "shift_type": "night",
                        "shift_type_label": "Nachtschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 16,
                        "supplements": {
                            "night_bonus": 15,
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 4,
                        "filled": 0,
                        "available": 4
                    },
                    "deadlines": {
                        "application": "2026-08-06T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1298-737e-bbfd-295992feb4db",
                    "title": "Küchenhilfe Großküche",
                    "description": "Wir suchen motivierte Küchenhilfe Großküche für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fad7-719c-887a-a0f510bbd31d",
                        "name": "IT Solutions GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "SV Gastronomie Wien",
                        "address": "Gablenzgasse 11, 1150 Wien",
                        "city": "Wien",
                        "postal_code": "1150",
                        "lat": 48.196,
                        "lng": 16.3268,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-12",
                        "end_date": "2026-12-03",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 13.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 10,
                        "filled": 0,
                        "available": 10
                    },
                    "deadlines": {
                        "application": "2026-08-07T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-12b2-72ff-8be7-dadbf8e167b6",
                    "title": "Kellner*in Haubenrestaurant",
                    "description": "Wir suchen motivierte Kellner*in Haubenrestaurant für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fca6-71cf-8683-c97f6f50a24a",
                        "name": "Reinigungsservice Alpin",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Restaurant Steirereck",
                        "address": "Am Heumarkt 2a, 1030 Wien",
                        "city": "Wien",
                        "postal_code": "1030",
                        "lat": 48.2014,
                        "lng": 16.387,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-15",
                        "end_date": "2026-10-04",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 15.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 3,
                        "filled": 0,
                        "available": 3
                    },
                    "deadlines": {
                        "application": "2026-08-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-12c7-72ab-bd18-53060b989f57",
                    "title": "Buffetkraft Messe",
                    "description": "Wir suchen motivierte Buffetkraft Messe für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fe56-70d1-bb3a-641339ce636a",
                        "name": "Lagerhaus Steiermark",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Reed Messe Wien",
                        "address": "Messeplatz 1, 1021 Wien",
                        "city": "Wien",
                        "postal_code": "1020",
                        "lat": 48.2176,
                        "lng": 16.4138,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-19",
                        "end_date": "2026-08-21",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 13.8,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 15,
                        "filled": 0,
                        "available": 15
                    },
                    "deadlines": {
                        "application": "2026-08-15T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-12d6-722c-8511-2aaa42b3d821",
                    "title": "Servicepersonal Weinbar",
                    "description": "Wir suchen motivierte Servicepersonal Weinbar für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b8-000d-719b-a73e-71665713a4fc",
                        "name": "Supermarkt AG",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Weinbar Graz",
                        "address": "Herrengasse 5, 8010 Graz",
                        "city": "Graz",
                        "postal_code": "8010",
                        "lat": 47.0707,
                        "lng": 15.4395,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-09",
                        "end_date": "2027-02-01",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 5,
                        "filled": 0,
                        "available": 5
                    },
                    "deadlines": {
                        "application": "2026-08-07T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-12f4-73f7-a675-8bf7331044c3",
                    "title": "Koch/Köchin Bistro",
                    "description": "Wir suchen motivierte Koch/Köchin Bistro für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-f4d3-71a1-89d4-ca7d5731b32a",
                        "name": "Wiener Gastro GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Bistro Donau",
                        "address": "Landstraße 30, 4020 Linz",
                        "city": "Linz",
                        "postal_code": "4020",
                        "lat": 48.3069,
                        "lng": 14.2858,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-17",
                        "end_date": "2026-11-03",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 15.8,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 2,
                        "filled": 0,
                        "available": 2
                    },
                    "deadlines": {
                        "application": "2026-08-14T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-130c-7006-942a-09ca3ab2b639",
                    "title": "Bankettkellner Festsaal",
                    "description": "Wir suchen motivierte Bankettkellner Festsaal für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fca6-71cf-8683-c97f6f50a24a",
                        "name": "Reinigungsservice Alpin",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Schlosshotel Mirabell",
                        "address": "Mirabellplatz 2, 5020 Salzburg",
                        "city": "Salzburg",
                        "postal_code": "5020",
                        "lat": 47.8095,
                        "lng": 13.055,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-13",
                        "end_date": "2026-09-19",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14.8,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 8,
                        "filled": 0,
                        "available": 8
                    },
                    "deadlines": {
                        "application": "2026-08-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-13e2-7390-a987-e12b987cdbb8",
                    "title": "Weihnachtsmarkt Servicekraft",
                    "description": "Wir suchen motivierte Weihnachtsmarkt Servicekraft für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                        "name": "Sicherheitsdienst Österreich",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Christkindlmarkt Rathausplatz",
                        "address": "Rathausplatz 1, 1010 Wien",
                        "city": "Wien",
                        "postal_code": "1010",
                        "lat": 48.2082,
                        "lng": 16.3739,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2027-01-02",
                        "end_date": "2027-02-01",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 30,
                        "filled": 0,
                        "available": 30
                    },
                    "deadlines": {
                        "application": "2026-12-29T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-14dd-7377-92d2-447578550d00",
                    "title": "Convention Coordinator",
                    "description": "Wir suchen engagierte Convention Coordinator zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                        "slug": "hotel",
                        "name": "Hotellerie",
                        "name_localized": "Hospitality",
                        "icon": "hotel",
                        "color": null
                    },
                    "tags": [
                        "Hospitality",
                        "Tagschicht"
                    ],
                    "organization": {
                        "id": "019fd2b8-000d-719b-a73e-71665713a4fc",
                        "name": "Supermarkt AG",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Austria Center Vienna",
                        "address": "Bruno-Kreisky-Platz 1, 1220 Wien",
                        "city": "Wien",
                        "postal_code": "1010",
                        "lat": 48.2082,
                        "lng": 16.3739,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-25",
                        "end_date": "2026-08-30",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 18,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 5,
                        "filled": 0,
                        "available": 5
                    },
                    "deadlines": {
                        "application": "2026-08-23T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1405-709f-bca9-b0a418fb6a75",
                    "title": "Silvesternacht Service",
                    "description": "Wir suchen motivierte Silvesternacht Service für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Nachtschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fe56-70d1-bb3a-641339ce636a",
                        "name": "Lagerhaus Steiermark",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Hotel Imperial Wien",
                        "address": "Kärntner Ring 16, 1015 Wien",
                        "city": "Wien",
                        "postal_code": "1010",
                        "lat": 48.2082,
                        "lng": 16.3739,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2027-02-09",
                        "end_date": "2027-02-10",
                        "shift_start_time": "22:00:00",
                        "shift_end_time": "06:00:00",
                        "shift_type": "night",
                        "shift_type_label": "Nachtschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 25,
                        "supplements": {
                            "night_bonus": 50,
                            "holiday_bonus": 100,
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 12,
                        "filled": 0,
                        "available": 12
                    },
                    "deadlines": {
                        "application": "2027-02-04T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1414-7267-ab60-1d28e4726b22",
                    "title": "Oktoberfest Kellner",
                    "description": "Wir suchen motivierte Oktoberfest Kellner für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b8-000d-719b-a73e-71665713a4fc",
                        "name": "Supermarkt AG",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Zelte Messe Graz",
                        "address": "Messeplatz 1, 8010 Graz",
                        "city": "Graz",
                        "postal_code": "8010",
                        "lat": 47.0707,
                        "lng": 15.4395,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-11-13",
                        "end_date": "2026-11-23",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 15,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 25,
                        "filled": 0,
                        "available": 25
                    },
                    "deadlines": {
                        "application": "2026-11-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1432-72a1-8757-db2b167892ea",
                    "title": "Pop-Up Restaurant Service",
                    "description": "Wir suchen motivierte Pop-Up Restaurant Service für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fad7-719c-887a-a0f510bbd31d",
                        "name": "IT Solutions GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Temporary Dining Wien",
                        "address": "Mariahilfer Straße 125, 1060 Wien",
                        "city": "Wien",
                        "postal_code": "1060",
                        "lat": 48.1985,
                        "lng": 16.359,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-09-04",
                        "end_date": "2026-10-04",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 6,
                        "filled": 0,
                        "available": 6
                    },
                    "deadlines": {
                        "application": "2026-09-02T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1441-71c8-a069-cb0d50c6165e",
                    "title": "Bierstandl Heuriger",
                    "description": "Wir suchen motivierte Bierstandl Heuriger für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-f768-7325-b254-280a67b1a995",
                        "name": "Retail Services GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Heuriger Mayer am Pfarrplatz",
                        "address": "Pfarrplatz 2, 1190 Wien",
                        "city": "Wien",
                        "postal_code": "1190",
                        "lat": 48.26,
                        "lng": 16.34,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-25",
                        "end_date": "2027-01-02",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 13.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 4,
                        "filled": 0,
                        "available": 4
                    },
                    "deadlines": {
                        "application": "2026-08-21T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-147c-7006-b991-64f8acb70696",
                    "title": "Nachtportier",
                    "description": "Wir suchen engagierte Nachtportier zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                        "slug": "hotel",
                        "name": "Hotellerie",
                        "name_localized": "Hospitality",
                        "icon": "hotel",
                        "color": null
                    },
                    "tags": [
                        "Hospitality",
                        "Nachtschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-fd7d-724b-8a64-b4836c66d92f",
                        "name": "Catering Deluxe GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "NH Salzburg City",
                        "address": "Franz-Josef-Straße 26, 5020 Salzburg",
                        "city": "Salzburg",
                        "postal_code": "5020",
                        "lat": 47.8063,
                        "lng": 13.0477,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-12",
                        "end_date": "2027-02-01",
                        "shift_start_time": "22:00:00",
                        "shift_end_time": "06:00:00",
                        "shift_type": "night",
                        "shift_type_label": "Nachtschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 15.5,
                        "supplements": {
                            "night_bonus": 15,
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 2,
                        "filled": 0,
                        "available": 2
                    },
                    "deadlines": {
                        "application": "2026-08-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1492-71be-933a-093e7178ae22",
                    "title": "Room Service Mitarbeiter",
                    "description": "Wir suchen engagierte Room Service Mitarbeiter zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                        "slug": "hotel",
                        "name": "Hotellerie",
                        "name_localized": "Hospitality",
                        "icon": "hotel",
                        "color": null
                    },
                    "tags": [
                        "Hospitality",
                        "Tagschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-f842-71ba-a318-a919a24d4bcf",
                        "name": "Hotel & Spa Imperial",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Hilton Vienna City",
                        "address": "Am Stadtpark 3, 1030 Wien",
                        "city": "Wien",
                        "postal_code": "1030",
                        "lat": 48.2006,
                        "lng": 16.3882,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-10",
                        "end_date": "2026-11-03",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 4,
                        "filled": 0,
                        "available": 4
                    },
                    "deadlines": {
                        "application": "2026-08-06T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-14a4-7258-8b10-6da8b27fb862",
                    "title": "Spa-Mitarbeiter*in",
                    "description": "Wir suchen engagierte Spa-Mitarbeiter*in zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                        "slug": "hotel",
                        "name": "Hotellerie",
                        "name_localized": "Hospitality",
                        "icon": "hotel",
                        "color": null
                    },
                    "tags": [
                        "Hospitality",
                        "Tagschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-f842-71ba-a318-a919a24d4bcf",
                        "name": "Hotel & Spa Imperial",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Schlosshotel Fuschl",
                        "address": "Schlossstraße 19, 5322 Hof bei Salzburg",
                        "city": "Salzburg",
                        "postal_code": "5020",
                        "lat": 47.8095,
                        "lng": 13.055,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-15",
                        "end_date": "2026-10-04",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 2,
                        "filled": 0,
                        "available": 2
                    },
                    "deadlines": {
                        "application": "2026-08-13T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1547-703f-bfe4-177b0923bd1d",
                    "title": "Concierge Nightshift",
                    "description": "Wir suchen engagierte Concierge Nightshift zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                        "slug": "hotel",
                        "name": "Hotellerie",
                        "name_localized": "Hospitality",
                        "icon": "hotel",
                        "color": null
                    },
                    "tags": [
                        "Hospitality",
                        "Nachtschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                        "name": "Caritas",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Grand Hotel Wien",
                        "address": "Kärntner Ring 9, 1010 Wien",
                        "city": "Wien",
                        "postal_code": "1010",
                        "lat": 48.2082,
                        "lng": 16.3739,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-20",
                        "end_date": "2027-08-05",
                        "shift_start_time": "22:00:00",
                        "shift_end_time": "06:00:00",
                        "shift_type": "night",
                        "shift_type_label": "Nachtschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 16,
                        "supplements": {
                            "night_bonus": 20,
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 1,
                        "filled": 0,
                        "available": 1
                    },
                    "deadlines": {
                        "application": "2026-08-17T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1560-70a6-abb3-020a87eccbc2",
                    "title": "Eventhelfer*in Musikfestival",
                    "description": "Für unsere bevorstehende Veranstaltung suchen wir tatkräftige Unterstützung. Als Eventhelfer*in Musikfestival sind Sie das Aushängeschild unserer Veranstaltung und sorgen für einen reibungslosen Ablauf.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                        "slug": "event",
                        "name": "Events",
                        "name_localized": "Events",
                        "icon": "calendar-star",
                        "color": null
                    },
                    "tags": [
                        "Events",
                        "Tagschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-fbb4-73fa-b801-e0c8a8de2651",
                        "name": "Bauwerk GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Donauinsel Festival",
                        "address": "Donauinsel, 1020 Wien",
                        "city": "Wien",
                        "postal_code": "1020",
                        "lat": 48.2112,
                        "lng": 16.4184,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-10-04",
                        "end_date": "2026-10-07",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 50,
                        "filled": 0,
                        "available": 50
                    },
                    "deadlines": {
                        "application": "2026-10-02T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:27+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            }
        ],
        "meta": {
            "total": 20,
            "algorithm_version": "1.0"
        }
    }
}
 

List the authenticated user's job applications.

requires authentication

Each row carries the application's full state — status timestamps, waitlist position, scheduling-conflict details, chosen shift dates and shift ids, rejection reason — plus whether it can still be withdrawn and, once selected, a summary of the assignments it produced with any contracts still awaiting the worker's signature.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/applications?status=pending&per_page=15" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": [
        \"withdrawn\"
    ],
    \"per_page\": 16
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/applications"
);

const params = {
    "status": "pending",
    "per_page": "15",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": [
        "withdrawn"
    ],
    "per_page": 16
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/applications';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'pending',
            'per_page' => '15',
        ],
        'json' => [
            'status' => ['withdrawn'],
            'per_page' => 16,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/applications')
      .replace(queryParameters: {
        'status': 'pending',
        'per_page': '15',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": [
        "withdrawn"
    ],
    "per_page": 16
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "applications": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 15,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/mobile/applications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

string|string[] Filter by one or more application statuses. Unknown statuses are refused. Example: pending

per_page   integer  optional    

Items per page (max 50). Example: 15

Body Parameters

status   string[]  optional    
Must be one of:
  • pending
  • shortlisted
  • selected
  • standby
  • rejected
  • withdrawn
  • expired
  • cancelled_by_org
  • job_cancelled
per_page   integer  optional    

Must be at least 1. Example: 16

Withdraw a Job Application

requires authentication

Takes back an application the company has not decided yet. Allowed while the application is pending, shortlisted or on the waitlist; a selected application holds live assignments and is released by cancelling those shifts instead, so it cannot be withdrawn here.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/applications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/withdraw" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Ich habe eine andere Stelle angenommen.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/applications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/withdraw"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Ich habe eine andere Stelle angenommen."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/applications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/withdraw';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Ich habe eine andere Stelle angenommen.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/applications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/withdraw');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Ich habe eine andere Stelle angenommen."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (409, No longer withdrawable):


{
    "status": "INVALID_OPERATION",
    "message": "Diese Bewerbung kann nicht mehr zurückgezogen werden."
}
 

Request      

POST api/v1/mobile/applications/{applicationId}/withdraw

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

applicationId   string     

The application to withdraw. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string  optional    

Optional reason, shown to the company. Example: Ich habe eine andere Stelle angenommen.

Shifts

List My Shifts

requires authentication

Returns the authenticated user's shifts (upcoming and past). Includes job and organization details, compensation info, and action flags.

Without a status filter, expired and cancelled entries are excluded; request them explicitly via status to see them.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts?status=signed&period=upcoming&per_page=15" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts"
);

const params = {
    "status": "signed",
    "period": "upcoming",
    "per_page": "15",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'signed',
            'period' => 'upcoming',
            'per_page' => '15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts')
      .replace(queryParameters: {
        'status': 'signed',
        'period': 'upcoming',
        'per_page': '15',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "shifts": [
            {
                "id": "019fd4e7-8db5-7390-a768-5cc04b8a5ecc",
                "status": "signed",
                "status_label": "Unterschrieben",
                "shift_date": "2026-08-07",
                "scheduled_start_time": "08:00:00",
                "scheduled_end_time": "16:00:00",
                "shift_type": "holiday",
                "confirmation_deadline": "2026-08-11T11:32:34+00:00",
                "notes": null,
                "location": {
                    "name": "Mohr-Bernier",
                    "address": "57499 Okuneva Springs",
                    "lat": 46.819652,
                    "lng": 9.690901
                },
                "compensation": {
                    "hourly_rate_gross": 32.63,
                    "estimated_gross": 230.33,
                    "currency": "EUR",
                    "supplements": {
                        "night": 0,
                        "weekend": 0
                    }
                },
                "can_confirm": false,
                "can_clock_in": false,
                "contact_person_name": null,
                "contact_person_phone": null,
                "contact_person_avatar_url": null,
                "meeting_point": null,
                "dress_code": "Vero amet fugiat quaerat odit.",
                "job": {
                    "id": "019fd4e7-8da0-718d-8b0a-7fa0f29523e2",
                    "title": "Loan Interviewer",
                    "category": "retail"
                },
                "organization": {
                    "id": "019fd4e7-5633-700f-ad54-9f03c0661fd1",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "contract": null
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 15,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/mobile/shifts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by shift status. One of: awaiting_signature, signed, checked_in, completed, hours_confirmed, no_show, cancelled, disputed. Example: signed

period   string  optional    

Which shifts to return: upcoming (the default), past, or all. Example: upcoming

per_page   integer  optional    

Items per page (max 50). Example: 15

Get Shift Calendar

requires authentication

Returns shifts organized for calendar display. Supports weekly view (default) and monthly view (±15 days or full month).

Weekly view: Returns shifts grouped by date for current week or specified week. Monthly view: Returns dates with shifts marked + shift list for that month.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/calendar?view=weekly" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/calendar"
);

const params = {
    "view": "weekly",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/calendar';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'view' => 'weekly',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/calendar')
      .replace(queryParameters: {
        'view': 'weekly',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Monthly):


{"status":"SUCCESS","data":{"view":"monthly","month":"2026-06","dates_with_shifts":["2026-06-18","2026-06-20","2026-06-25"],"calendar_weeks":[[...]],"shifts":[...]}}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "view": "weekly",
        "date_range": {
            "start": "2026-08-03",
            "end": "2026-08-09"
        },
        "dates_with_shifts": [
            "2026-08-04",
            "2026-08-05",
            "2026-08-06",
            "2026-08-07"
        ],
        "shifts_by_date": {
            "2026-08-04": [
                {
                    "id": "019fd4e7-8dc3-7045-999d-0b5016e16805",
                    "status": "completed",
                    "status_label": "Abgeschlossen",
                    "shift_date": "2026-08-04",
                    "day_of_week": "Tuesday",
                    "scheduled_start_time": "08:17:00",
                    "scheduled_end_time": "09:08:00",
                    "scheduled_hours": null,
                    "shift_type": "day",
                    "location": {
                        "name": "Beatty Ltd",
                        "address": "780 Vergie Courts"
                    },
                    "compensation": {
                        "hourly_rate_gross": 30.4,
                        "total_gross": 251.38
                    },
                    "badges": {
                        "1": "3_more_shifts"
                    },
                    "job": {
                        "id": "019fd4e7-8da0-718d-8b0a-7fa0f29523e2",
                        "title": "Loan Interviewer",
                        "category": "retail"
                    },
                    "organization": {
                        "id": "019fd4e7-5633-700f-ad54-9f03c0661fd1",
                        "name": "Caritas",
                        "logo_url": null
                    }
                }
            ],
            "2026-08-05": [
                {
                    "id": "019fd4e7-8dae-73bd-8647-588dbb86c1a6",
                    "status": "awaiting_signature",
                    "status_label": "Unterschrift ausstehend",
                    "shift_date": "2026-08-05",
                    "day_of_week": "Wednesday",
                    "scheduled_start_time": "19:33:00",
                    "scheduled_end_time": "06:36:00",
                    "scheduled_hours": null,
                    "shift_type": "day",
                    "location": {
                        "name": "Marvin and Sons",
                        "address": "886 Schimmel Plaza Suite 357"
                    },
                    "compensation": {
                        "hourly_rate_gross": 16.24,
                        "total_gross": 132.68
                    },
                    "badges": {
                        "1": "2_more_shifts"
                    },
                    "job": {
                        "id": "019fd4e7-8da0-718d-8b0a-7fa0f29523e2",
                        "title": "Loan Interviewer",
                        "category": "retail"
                    },
                    "organization": {
                        "id": "019fd4e7-5633-700f-ad54-9f03c0661fd1",
                        "name": "Caritas",
                        "logo_url": null
                    }
                }
            ],
            "2026-08-06": [
                {
                    "id": "019fd4e7-8dbb-71a3-bba2-05174a59c0ad",
                    "status": "checked_in",
                    "status_label": "Eingecheckt",
                    "shift_date": "2026-08-06",
                    "day_of_week": "Thursday",
                    "scheduled_start_time": "00:19:00",
                    "scheduled_end_time": "23:37:00",
                    "scheduled_hours": null,
                    "shift_type": "holiday",
                    "location": {
                        "name": "Harris-Klocko",
                        "address": "15800 Grant Shore Apt. 974"
                    },
                    "compensation": {
                        "hourly_rate_gross": 17.81,
                        "total_gross": 163.15
                    },
                    "badges": {
                        "1": "1_more_shifts"
                    },
                    "job": {
                        "id": "019fd4e7-8da0-718d-8b0a-7fa0f29523e2",
                        "title": "Loan Interviewer",
                        "category": "retail"
                    },
                    "organization": {
                        "id": "019fd4e7-5633-700f-ad54-9f03c0661fd1",
                        "name": "Caritas",
                        "logo_url": null
                    }
                }
            ],
            "2026-08-07": [
                {
                    "id": "019fd4e7-8db5-7390-a768-5cc04b8a5ecc",
                    "status": "signed",
                    "status_label": "Unterschrieben",
                    "shift_date": "2026-08-07",
                    "day_of_week": "Friday",
                    "scheduled_start_time": "08:00:00",
                    "scheduled_end_time": "16:00:00",
                    "scheduled_hours": null,
                    "shift_type": "holiday",
                    "location": {
                        "name": "Mohr-Bernier",
                        "address": "57499 Okuneva Springs"
                    },
                    "compensation": {
                        "hourly_rate_gross": 32.63,
                        "total_gross": 230.33
                    },
                    "badges": [
                        "last_shift"
                    ],
                    "job": {
                        "id": "019fd4e7-8da0-718d-8b0a-7fa0f29523e2",
                        "title": "Loan Interviewer",
                        "category": "retail"
                    },
                    "organization": {
                        "id": "019fd4e7-5633-700f-ad54-9f03c0661fd1",
                        "name": "Caritas",
                        "logo_url": null
                    }
                }
            ]
        },
        "totals": {
            "shifts_count": 4,
            "total_hours": 0,
            "total_earnings_gross": 777.5400000000001
        },
        "navigation": {
            "prev_week": "2026-07-27",
            "next_week": "2026-08-10"
        }
    }
}
 

Request      

GET api/v1/mobile/shifts/calendar

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

view   string  optional    

Calendar view: weekly or monthly. Example: weekly

date   string  optional    

date Reference date. Defaults to today, which is what the app wants on open.

month   string  optional    

Month for the monthly view (YYYY-MM). Defaults to the current month.

Get Shift Readiness

requires authentication

Returns whether the authenticated employee is ready to clock in for a specific shift, plus a list of any blocking conditions.

Readiness checks (in order):

  1. Shift belongs to the authenticated employee
  2. Shift status is signed (not yet clocked in)
  3. Shift date is today (Austrian time)
  4. Current time is within the clock-in window: 30 minutes before scheduled_start_time up to 30 minutes after end_time
  5. No active attendance record already exists (prevents double clock-in)

The response always returns HTTP 200; ready indicates whether the employee may proceed to clock in.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/readiness" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/readiness"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/readiness';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/readiness');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Not Ready):


{
    "status": "SUCCESS",
    "data": {
        "ready": false,
        "shift_id": "9c3f5f3d-0123-4abc-b456-426614174000",
        "shift_date": "2026-06-28",
        "scheduled_start_time": "08:00",
        "scheduled_end_time": "16:00",
        "blockers": [
            "not_today",
            "outside_clock_in_window"
        ]
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "ready": false,
        "shift_id": "019fd4e7-8dae-73bd-8647-588dbb86c1a6",
        "shift_date": "2026-08-05",
        "scheduled_start_time": "19:33:00",
        "scheduled_end_time": "06:36:00",
        "blockers": [
            "invalid_status",
            "contract_unsigned",
            "rahmenvertrag_unsigned",
            "ecard_missing",
            "not_today",
            "outside_clock_in_window"
        ]
    }
}
 

Example response (403):


{
    "status": "ERROR",
    "message": "Diese Schicht gehört nicht zu deinem Konto."
}
 

Example response (404):


{
    "status": "ERROR",
    "message": "Schicht nicht gefunden."
}
 

Request      

GET api/v1/mobile/shifts/{shiftId}/readiness

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

UUID of the shift. Example: 9c3f5f3d-0123-4abc-b456-426614174000

Get Shift

requires authentication

Everything the shift screen needs while a shift is running: the booking itself, where it is, who to ask for, and the clock.

Until now a shift could be listed and acted on but not fetched, so a screen opened on one had to keep whatever the list gave it and could not refresh after a clock-in or a break.

attendance.worked_seconds is time on the clock with breaks already deducted — the client counts on from it rather than polling. While a break is open, current_break_seconds does the same for the break, and worked_seconds stops advancing.

actions says which of the controls apply right now, decided by the same rules the clock-in, clock-out and break endpoints enforce, so the screen does not have to reimplement them and then disagree.

contract is the Überlassungsvertrag for this shift alone. Each shift is agreed to separately, because a company can set terms on a single shift, so a worker taking three days signs three contracts and each has its own sign_deadline. Its id is what GET /mobile/contracts/{contractId} and POST .../sign take, so the screen can open or sign the right one without searching the worker's contract list. It is null before the company has selected the worker.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "shift": {
            "id": "019fd4e7-8dae-73bd-8647-588dbb86c1a6",
            "status": "awaiting_signature",
            "status_label": "Unterschrift ausstehend",
            "shift_date": "2026-08-05",
            "scheduled_start_time": "19:33:00",
            "scheduled_end_time": "06:36:00",
            "shift_type": "day",
            "confirmation_deadline": "2026-08-23T19:02:29+00:00",
            "notes": null,
            "location": {
                "name": "Marvin and Sons",
                "address": "886 Schimmel Plaza Suite 357",
                "lat": 48.035945,
                "lng": 14.889039
            },
            "compensation": {
                "hourly_rate_gross": 16.24,
                "estimated_gross": 132.68,
                "currency": "EUR",
                "supplements": {
                    "night": 0,
                    "weekend": 0
                }
            },
            "can_confirm": true,
            "can_clock_in": false,
            "contact_person_name": null,
            "contact_person_phone": null,
            "contact_person_avatar_url": null,
            "meeting_point": null,
            "dress_code": "Vero amet fugiat quaerat odit.",
            "job": {
                "id": "019fd4e7-8da0-718d-8b0a-7fa0f29523e2",
                "title": "Loan Interviewer",
                "category": "retail"
            },
            "organization": {
                "id": "019fd4e7-5633-700f-ad54-9f03c0661fd1",
                "name": "Caritas",
                "logo_url": null,
                "average_rating": null
            },
            "contract": {
                "id": "019fd4e7-8db1-7290-97a1-e17bfbb64a4d",
                "contract_number": "UEVG-556708",
                "status": "pending_signature",
                "status_label": "Unterschrift ausstehend",
                "is_signed": false,
                "can_sign": true,
                "signed_at": null,
                "sign_deadline": "2026-08-07T02:29:32+00:00",
                "is_overdue": false
            },
            "planned_break_minutes": 0,
            "paid_break_minutes": 30,
            "attendance": {
                "clock_in_at": null,
                "clock_out_at": null,
                "worked_seconds": 0,
                "break_minutes_taken": 0,
                "is_on_break": false,
                "current_break_started_at": null,
                "current_break_seconds": 0
            },
            "actions": {
                "can_clock_in": false,
                "can_clock_out": false,
                "can_start_break": false,
                "can_end_break": false,
                "can_report_incident": false,
                "can_change_workplace": false,
                "can_sign_contract": true
            }
        }
    }
}
 

Example response (404, Not the caller's shift):


{
    "status": "RESOURCE_NOT_FOUND",
    "message": "Die Schicht wurde nicht gefunden."
}
 

Request      

GET api/v1/mobile/shifts/{shiftId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

The assignment to read. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Report Shift Incident

requires authentication

Allows an employee to report a workplace incident that occurred during an active or recently completed shift (injury, safety concern, equipment failure, etc.). Creates a support ticket of category shift linked to the given shift.

The incident report is sent to the internal team for follow-up. Austrian ASchG §15 obliges employers/employees to report workplace injuries.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/incident" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"incident_type\": \"injury\",
    \"description\": \"I slipped on a wet floor near the loading bay.\",
    \"occurred_at\": \"2026-06-27T14:30:00Z\",
    \"lat\": 48.2082,
    \"lng\": 16.3738
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/incident"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "incident_type": "injury",
    "description": "I slipped on a wet floor near the loading bay.",
    "occurred_at": "2026-06-27T14:30:00Z",
    "lat": 48.2082,
    "lng": 16.3738
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/incident';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'incident_type' => 'injury',
            'description' => 'I slipped on a wet floor near the loading bay.',
            'occurred_at' => '2026-06-27T14:30:00Z',
            'lat' => 48.2082,
            'lng' => 16.3738,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/incident');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "incident_type": "injury",
    "description": "I slipped on a wet floor near the loading bay.",
    "occurred_at": "2026-06-27T14:30:00Z",
    "lat": 48.2082,
    "lng": 16.3738
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "ticket_id": "019fd2b8-3a07-70e6-be52-a12bdc110c54",
        "ticket_number": "TKT-202608-00001",
        "message": "Vorfall wurde gemeldet."
    }
}
 

Example response (403):


{
    "status": "ERROR",
    "message": "Diese Schicht gehört nicht zu deinem Konto."
}
 

Example response (404):


{
    "status": "ERROR",
    "message": "Schicht nicht gefunden."
}
 

Example response (422):


{
    "status": "ERROR",
    "message": "The description field is required."
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/incident

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

UUID of the shift. Example: 9c3f5f3d-0123-4abc-b456-426614174000

Body Parameters

incident_type   string     

Type of incident: Allowed: injury, safety, equipment, other. Example: injury

description   string     

Description of the incident (20–5000 chars). Example: I slipped on a wet floor near the loading bay.

occurred_at   string  optional    

nullable Timestamp when the incident occurred (ISO 8601). Example: 2026-06-27T14:30:00Z

lat   number  optional    

nullable Where the worker was when filing the report. Falls back to the assignment's last location ping when omitted. Example: 48.2082

lng   number  optional    

nullable Example: 16.3738

Confirm My Hours

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "clock_in_at": "2026-08-06T02:30:01+00:00",
        "clock_out_at": "2026-08-06T10:30:01+00:00",
        "break_minutes": 480,
        "scheduled_end_time": "10:30:00",
        "payable_minutes": 30,
        "unclaimed_overtime_minutes": 0,
        "confirmed_at": null
    }
}
 

Request      

GET api/v1/mobile/shifts/{shiftId}/hours-confirmation

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Confirm My Hours

requires authentication

The worker's sign-off on what they will be paid for, after clocking out.

A GET returns the figures the screen shows: when they actually clocked in and out, how many of those minutes are payable, and how many fall outside the cap and would need an overtime request. The POST records their agreement, with an optional signature.

Confirming does not settle anything — the company still confirms the hours in its own Hours screen, and that is what reaches payroll. This records the worker's side of the story so a later disagreement has both.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/hours-confirmation');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your hours have been confirmed.",
    "data": {
        "clock_in_at": "2026-08-05T16:19:02+00:00",
        "clock_out_at": "2026-08-06T00:19:02+00:00",
        "break_minutes": 480,
        "scheduled_end_time": "00:19:00",
        "payable_minutes": 30,
        "unclaimed_overtime_minutes": 0,
        "confirmed_at": "2026-08-06T00:19:02+00:00"
    }
}
 

Example response (422, Not clocked out):


{
    "status": "INVALID_OPERATION",
    "message": "You have not clocked out of this shift yet."
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/hours-confirmation

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

The assignment to confirm. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

signature   string  optional    

Base64 signature drawn on the confirmation screen.

List My Shift Requests

requires authentication

Every request the authenticated worker has raised, newest first, optionally narrowed to one shift or one status.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/requests?shift_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&status=pending" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"shift_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"status\": \"withdrawn\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/requests"
);

const params = {
    "shift_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "status": "pending",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "shift_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "status": "withdrawn"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'shift_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'status' => 'pending',
        ],
        'json' => [
            'shift_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
            'status' => 'withdrawn',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/requests')
      .replace(queryParameters: {
        'shift_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'status': 'pending',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "shift_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "status": "withdrawn"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "requests": [
            {
                "id": "019fd2b8-a28b-7060-8491-a3fa2aee9676",
                "assignment_id": "019fd2b8-a219-710c-9a2a-91af501576cd",
                "type": "overtime",
                "type_label": "Überstunden",
                "affects_pay": true,
                "status": "pending",
                "status_label": "Offen",
                "reason_code": null,
                "reason_note": "Die Lieferung musste noch fertig verräumt werden.",
                "claimed_minutes": 45,
                "evidence": [],
                "requested_at": "2026-08-06T00:19:03+00:00",
                "decision": null,
                "escalated_at": null,
                "conversation_id": null
            },
            {
                "id": "019fd2b8-a252-73da-94fd-15b16c76b745",
                "assignment_id": "019fd2b8-a219-710c-9a2a-91af501576cd",
                "type": "manual_check_in",
                "type_label": "Manuelle Anmeldung",
                "affects_pay": false,
                "status": "pending",
                "status_label": "Offen",
                "reason_code": "no_qr_scan",
                "reason_note": null,
                "claimed_minutes": null,
                "evidence": [],
                "requested_at": "2026-08-05T16:19:03+00:00",
                "decision": null,
                "escalated_at": null,
                "conversation_id": null
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

shift_id   string  optional    

Only requests against this assignment. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

status   string  optional    

Allowed: pending, approved, declined, withdrawn. Example: pending

Body Parameters

shift_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

status   string  optional    

Example: withdrawn

Must be one of:
  • pending
  • approved
  • declined
  • withdrawn

Raise a Shift Request

requires authentication

Asks the company to decide something about a shift the worker was assigned to — extra time worked, an early departure, a correction to the recorded hours, or evidence excusing an absence.

Nothing here blocks the worker: an overtime request is raised after they have already clocked out, and the recorded clock-out time is never altered by it. What the request can change is how many of those minutes are paid.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"overtime\",
    \"reason_code\": \"covered_for_colleague\",
    \"reason_note\": \"Stayed to finish the late delivery.\",
    \"claimed_minutes\": 45,
    \"evidence\": [
        \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
    ]
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "overtime",
    "reason_code": "covered_for_colleague",
    "reason_note": "Stayed to finish the late delivery.",
    "claimed_minutes": 45,
    "evidence": [
        "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/requests';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'overtime',
            'reason_code' => 'covered_for_colleague',
            'reason_note' => 'Stayed to finish the late delivery.',
            'claimed_minutes' => 45,
            'evidence' => ['019f9939-c4c3-70fb-a54e-5ebf63db36d2'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "type": "overtime",
    "reason_code": "covered_for_colleague",
    "reason_note": "Stayed to finish the late delivery.",
    "claimed_minutes": 45,
    "evidence": [
        "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
    ]
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your request has been sent to the company.",
    "data": {
        "id": "019fd2b8-a28b-7060-8491-a3fa2aee9676",
        "assignment_id": "019fd2b8-a219-710c-9a2a-91af501576cd",
        "type": "overtime",
        "type_label": "Überstunden",
        "affects_pay": true,
        "status": "pending",
        "status_label": "Offen",
        "reason_code": null,
        "reason_note": "Die Lieferung musste noch fertig verräumt werden.",
        "claimed_minutes": 45,
        "evidence": [],
        "requested_at": "2026-08-06T00:19:03+00:00",
        "decision": null,
        "escalated_at": null,
        "conversation_id": null
    }
}
 

Example response (422, Already open):


{
    "status": "VALIDATION_ERROR",
    "message": "A request of this type is already open for this shift."
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

The assignment the request concerns. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

type   string     

Allowed: overtime, early_end, hours_correction, manual_check_in, absence_excuse, workplace_change. Example: overtime

reason_code   string  optional    

A machine-readable reason the app can translate. Example: covered_for_colleague

reason_note   string  optional    

The worker's own description. Example: Stayed to finish the late delivery.

claimed_minutes   integer  optional    

Extra minutes claimed (overtime and hours corrections). Example: 45

evidence   string[]  optional    

Ids of shift attachments backing the claim.

Withdraw a Shift Request

requires authentication

Takes back a request the company has not decided yet. Once decided it stands — the record of what was asked and what was answered is part of the pay trail and is not erasable by either side.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (409, Already decided):


{
    "status": "INVALID_OPERATION",
    "message": "This request has already been decided."
}
 

Request      

DELETE api/v1/mobile/requests/{requestId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

requestId   string     

The request to withdraw. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Record Workplace Change

requires authentication

Records a mid-shift workplace change discovered by the employee (different employer, different location, or different working conditions than those agreed in the Überlassungsbestätigung).

Per BUAG §3 and AÜG §11, employees must be informed about and consent to workplace changes. This action stores the change event in the workplace_changes JSONB column added by migration M1.

The employee must be actively clocked in for this endpoint to accept changes.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/workplace-change" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"change_type\": \"location\",
    \"old_value\": \"Hauptstraße 1, 1010 Wien\",
    \"new_value\": \"Mariahilfer Str. 50, 1060 Wien\",
    \"reason\": \"Production line moved.\",
    \"consent_given\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/workplace-change"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "change_type": "location",
    "old_value": "Hauptstraße 1, 1010 Wien",
    "new_value": "Mariahilfer Str. 50, 1060 Wien",
    "reason": "Production line moved.",
    "consent_given": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/workplace-change';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'change_type' => 'location',
            'old_value' => 'Hauptstraße 1, 1010 Wien',
            'new_value' => 'Mariahilfer Str. 50, 1060 Wien',
            'reason' => 'Production line moved.',
            'consent_given' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/9c3f5f3d-0123-4abc-b456-426614174000/workplace-change');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "change_type": "location",
    "old_value": "Hauptstraße 1, 1010 Wien",
    "new_value": "Mariahilfer Str. 50, 1060 Wien",
    "reason": "Production line moved.",
    "consent_given": true
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200):


{
    "status": "SUCCESS",
    "data": {
        "message": "Arbeitsortänderung wurde erfasst.",
        "total_changes": 1
    }
}
 

Example response (400):


{
    "status": "ERROR",
    "message": "Du bist nicht eingestempelt."
}
 

Example response (403):


{
    "status": "ERROR",
    "message": "Diese Schicht gehört nicht zu deinem Konto."
}
 

Example response (404):


{
    "status": "ERROR",
    "message": "Schicht nicht gefunden."
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/workplace-change

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

UUID of the shift. Example: 9c3f5f3d-0123-4abc-b456-426614174000

Body Parameters

change_type   string     

Type of change: Allowed: employer, location, conditions, other. Example: location

old_value   string     

Previous value (e.g. old address). Example: Hauptstraße 1, 1010 Wien

new_value   string     

New value after change. Example: Mariahilfer Str. 50, 1060 Wien

reason   string  optional    

nullable Free-text reason for the change. Example: Production line moved.

consent_given   boolean     

Employee confirms they were informed and consent. Example: true

Cancel Shift

requires authentication

The worker cancels their own shift. What that costs depends on whether they had signed:

Unsigned (awaiting_signature) — declining. The worker is refusing to sign, not breaking a commitment, so it is free at any time: no 24-hour window, no Stornopauschale, no reliability entry. Letting the sign deadline lapse costs nothing, and saying no outright cannot cost more.

Signed — cancelling. Within 24 hours of start it is refused (DEADLINE_PASSED — contact support); 24–48 hours' notice carries the Stornopauschale and a reliability penalty; more than 48 hours is penalty-free.

Either way the shift's own Überlassungsvertrag is revoked with it, so nothing signable (or in force) survives on a cancelled shift.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The shift has been cancelled. Your reliability score may be affected.",
    "data": {
        "shift_id": "019fd359-b78c-730d-810e-166b221166a5",
        "cancelled_at": "2026-08-05 19:15",
        "notice_hours": 0,
        "reliability_impact": 0
    }
}
 

Example response (422, Signed, under 24 hours to start):


{
    "status": "DEADLINE_PASSED",
    "message": "Diese Schicht kann weniger als 24 Stunden vor Beginn nicht mehr selbst storniert werden. Bitte kontaktieren Sie den Support."
}
 

Example response (422, Already cancelled or completed):


{
    "status": "INVALID_OPERATION",
    "message": "Diese Schicht kann nicht storniert werden."
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/cancel

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Why the worker is dropping the shift. 10–500 characters. Example: Krankheitsbedingt kurzfristig abgesagt.

Clock In

requires authentication

Clock in to a shift.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2

A QR scan proves presence by itself. A **manual or GPS check-in** must
bring its own proof: `lat`/`lng` are required and checked against the
workplace — beyond the configured radius (500 m) the check-in is
refused with `LOCATION_OUT_OF_RANGE` — and at least one photo taken on
site must accompany the request. The photos are stored as shift
attachments and put in front of the company together with the
check-in review.

With `gps_unavailable: true` the position requirement is waived, the
check-in is flagged for support, and the photos remain mandatory./clock-in" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"lat\": 48.2082,
    \"lng\": 16.3738,
    \"method\": \"manual\",
    \"signature\": \"Beispieltext\",
    \"photos\": null,
    \"gps_unavailable\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2

A QR scan proves presence by itself. A **manual or GPS check-in** must
bring its own proof: `lat`/`lng` are required and checked against the
workplace — beyond the configured radius (500 m) the check-in is
refused with `LOCATION_OUT_OF_RANGE` — and at least one photo taken on
site must accompany the request. The photos are stored as shift
attachments and put in front of the company together with the
check-in review.

With `gps_unavailable: true` the position requirement is waived, the
check-in is flagged for support, and the photos remain mandatory./clock-in"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "lat": 48.2082,
    "lng": 16.3738,
    "method": "manual",
    "signature": "Beispieltext",
    "photos": null,
    "gps_unavailable": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2

A QR scan proves presence by itself. A **manual or GPS check-in** must
bring its own proof: `lat`/`lng` are required and checked against the
workplace — beyond the configured radius (500 m) the check-in is
refused with `LOCATION_OUT_OF_RANGE` — and at least one photo taken on
site must accompany the request. The photos are stored as shift
attachments and put in front of the company together with the
check-in review.

With `gps_unavailable: true` the position requirement is waived, the
check-in is flagged for support, and the photos remain mandatory./clock-in';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'lat' => 48.2082,
            'lng' => 16.3738,
            'method' => 'manual',
            'signature' => 'Beispieltext',
            'photos' => null,
            'gps_unavailable' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2

A QR scan proves presence by itself. A **manual or GPS check-in** must
bring its own proof: `lat`/`lng` are required and checked against the
workplace — beyond the configured radius (500 m) the check-in is
refused with `LOCATION_OUT_OF_RANGE` — and at least one photo taken on
site must accompany the request. The photos are stored as shift
attachments and put in front of the company together with the
check-in review.

With `gps_unavailable: true` the position requirement is waived, the
check-in is flagged for support, and the photos remain mandatory./clock-in');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "lat": 48.2082,
    "lng": 16.3738,
    "method": "manual",
    "signature": "Beispieltext",
    "photos": null,
    "gps_unavailable": false
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "You have successfully clocked in.",
    "data": {
        "attendance": {
            "id": "019fd515-53b8-73b9-8f04-c3758917dbf3",
            "clock_in_at": "2026-08-06T03:19:32+00:00",
            "clock_in_method": "manual"
        },
        "shift": {
            "id": "019fd515-5399-73cd-ba1c-6d51fdd33ee7",
            "status": "checked_in"
        },
        "conversation_id": "019fd515-53c3-70a6-93d3-1451aec0adfa",
        "gps_fallback": false,
        "support_notified": false
    }
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/clock-in

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: `019f9939-c4c3-70fb-a54e-5ebf63db36d2

A QR scan proves presence by itself. A manual or GPS check-in must bring its own proof: lat/lng are required and checked against the workplace — beyond the configured radius (500 m) the check-in is refused with LOCATION_OUT_OF_RANGE — and at least one photo taken on site must accompany the request. The photos are stored as shift attachments and put in front of the company together with the check-in review.

With gps_unavailable: true the position requirement is waived, the check-in is flagged for support, and the photos remain mandatory.`

Body Parameters

lat   number  optional    

Required for manual/gps check-ins (unless gps_unavailable). Example: 48.2082

lng   number  optional    

Required for manual/gps check-ins (unless gps_unavailable). Example: 16.3738

method   string  optional    

Allowed: qr, gps, manual, nfc. Example: manual

signature   string  optional    

Example: Beispieltext

photos   file[]     

Photo proof, 1–4 images (JPEG/PNG/WebP), required for manual/gps check-ins — sent as multipart.

gps_unavailable   boolean  optional    

GPS unavailable on the device — support is notified; photos stay required. Example: false

Clock Out

requires authentication

Clock out from a shift.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/clock-out" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"lat\": 1,
    \"lng\": 1,
    \"method\": \"qr\",
    \"break_minutes\": 1,
    \"signature\": \"Beispieltext\",
    \"early_checkout_reason\": \"Krankheitsbedingt kurzfristig abgesagt.\",
    \"early_checkout_category\": \"sickness\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/clock-out"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "lat": 1,
    "lng": 1,
    "method": "qr",
    "break_minutes": 1,
    "signature": "Beispieltext",
    "early_checkout_reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "early_checkout_category": "sickness"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/clock-out';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'lat' => 1,
            'lng' => 1,
            'method' => 'qr',
            'break_minutes' => 1,
            'signature' => 'Beispieltext',
            'early_checkout_reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
            'early_checkout_category' => 'sickness',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/clock-out');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "lat": 1,
    "lng": 1,
    "method": "qr",
    "break_minutes": 1,
    "signature": "Beispieltext",
    "early_checkout_reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "early_checkout_category": "sickness"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "You have successfully clocked out.",
    "data": {
        "attendance": {
            "id": "019fd2b8-9e87-7131-9457-cc5bea1d6e78",
            "clock_in_at": "2026-08-05T16:19:02+00:00",
            "clock_out_at": "2026-08-06T00:19:02+00:00",
            "total_hours_worked": 8,
            "break_minutes": 480,
            "overtime_hours": 0,
            "actual_gross_amount": 0
        },
        "shift": {
            "id": "019fd2b8-9e68-73ba-aff1-e1ff0de2c556",
            "status": "completed"
        }
    }
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/clock-out

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

lat   integer  optional    

Example: 1

lng   integer  optional    

Example: 1

method   string  optional    

Allowed: qr, gps, manual, nfc. Example: qr

break_minutes   integer  optional    

Example: 1

signature   string  optional    

Example: Beispieltext

early_checkout_reason   string  optional    

Example: Krankheitsbedingt kurzfristig abgesagt.

early_checkout_category   string  optional    

Allowed: sickness, injury, misconduct, left_workplace, employer_ended, other. Example: sickness

Toggle Break

requires authentication

Employee starts or ends a break with a simple button tap.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/break" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/break"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/break';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/break');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "action": "break_started",
        "break_id": "019fd2b8-9e9d-724c-8182-9d92c82b5d24",
        "started_at": "2026-08-05T16:19:02+00:00",
        "break_type": "standard",
        "message": "Pause gestartet"
    },
    "meta": {
        "is_on_break": true,
        "total_break_minutes": 0,
        "mandatory_break_taken": false
    }
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/break

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Upload a photo / document attached to a shift (evidence, handover, …).

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/attachments" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "caption=Übergabeprotokoll"\
    --form "file=@/tmp/phpr29e3bd3d8h38azIbXa" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/attachments"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('caption', 'Übergabeprotokoll');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/attachments';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'caption',
                'contents' => 'Übergabeprotokoll'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/phpr29e3bd3d8h38azIbXa', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/attachments');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.fields['caption'] = 'Übergabeprotokoll';
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/phpr29e3bd3d8h38azIbXa'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The attachment has been uploaded.",
    "data": {
        "attachment": {
            "id": "019fd2b8-3a7e-7235-9631-d728b029f2ea",
            "original_filename": "beispiel.pdf",
            "mime_type": "application/pdf",
            "file_size": 65536,
            "caption": "Übergabeprotokoll",
            "created_at": "2026-08-05T16:18:36+00:00"
        }
    }
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/attachments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Body Parameters

file   file     

The photo or document. Example: /tmp/phpr29e3bd3d8h38azIbXa

caption   string  optional    

Optional short description. Example: Übergabeprotokoll

Generate Check In Qr

requires authentication

Generate a QR code payload for shift check-in/check-out.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/qr?action=architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"action\": \"clock_in\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/qr"
);

const params = {
    "action": "architecto",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "action": "clock_in"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/qr';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'action' => 'architecto',
        ],
        'json' => [
            'action' => 'clock_in',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/qr')
      .replace(queryParameters: {
        'action': 'architecto',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "action": "clock_in"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "messages.qr_generated",
    "data": {
        "qr_payload": "eyJpdiI6IkxPQXdSYmJ4akVDZGRldzhseWNTVnc9PSIsInZhbHVlIjoiaWNZc1FaMzk4QWRHN0RFR0M5WkNKU2NKcTNzN2tRSVZtSUJOcEdSLzR1TnAwUUxTamY0V0pWWWc2ZkNhQUYzak9NZzlnb0tVc1E0bXhIN1BXa2ZwZUR1SzlTTjZvUlpNSENUcWdZcGFUZFJsdVZZZldnOTFNNmRPa3JWYkJoUzZiTzFJR3Z2dFVDMlMyVnNENFpMMXViU2w2VGtYdWx1TnljWGZZQUlLZEo1UytvOHZ0OTVUZndJa0FvZnMrV28wIiwibWFjIjoiNTc4ZjhlZWQ1MTdhYTNhOTMzOGQ3MDAzMDU4YmYyMDNjNjgwYWMxNTZhZWE5YmI4MjYwOTc5MjljNzkxYTUxNiIsInRhZyI6IiJ9",
        "expires_at": "2026-08-05T16:19:36+00:00",
        "action": "clock_in",
        "action_label": "Einchecken"
    }
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/qr

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Query Parameters

action   string     

The action type: clock_in, clock_out, break_start, break_end Allowed: clock_in, clock_out, break_start, break_end. Example: architecto

Body Parameters

action   string     

Allowed: clock_in, clock_out, break_start, break_end. Example: clock_in

Get Dienstzettel

requires authentication

Stream the per-shift Dienstzettel as a PDF.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/dienstzettel" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/dienstzettel"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/dienstzettel';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/dienstzettel');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "43ecbfa4-7afc-4a80-8897-2927ce9ef626",
        "timestamp": "2026-08-21T05:14:34.017781Z"
    }
}
 

Request      

GET api/v1/mobile/shifts/{shiftId}/dienstzettel

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Get Earnings Statement

requires authentication

Stream our own per-shift earnings statement (Verdienstabrechnung) as a PDF.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/earnings-statement" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/earnings-statement"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/earnings-statement';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/earnings-statement');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "7cd5c56f-d7e8-4b71-9899-3087a486211c",
        "timestamp": "2026-08-21T05:14:34.030330Z"
    }
}
 

Request      

GET api/v1/mobile/shifts/{shiftId}/earnings-statement

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Rate Company

requires authentication

Employee submits a rating for a company after shift completion.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"overall_score\": 1,
    \"punctuality_score\": 1,
    \"professionalism_score\": 1,
    \"communication_score\": 1,
    \"work_environment_score\": 1,
    \"comment\": \"Beispieltext\",
    \"is_anonymous\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "overall_score": 1,
    "punctuality_score": 1,
    "professionalism_score": 1,
    "communication_score": 1,
    "work_environment_score": 1,
    "comment": "Beispieltext",
    "is_anonymous": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'overall_score' => 1,
            'punctuality_score' => 1,
            'professionalism_score' => 1,
            'communication_score' => 1,
            'work_environment_score' => 1,
            'comment' => 'Beispieltext',
            'is_anonymous' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "overall_score": 1,
    "punctuality_score": 1,
    "professionalism_score": 1,
    "communication_score": 1,
    "work_environment_score": 1,
    "comment": "Beispieltext",
    "is_anonymous": true
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your rating has been submitted successfully.",
    "data": {
        "rating": {
            "id": "019fd2b8-3ad6-716f-9be2-5f73c4f494f2",
            "overall_score": 1,
            "created_at": "2026-08-05 16:18"
        }
    }
}
 

Request      

POST api/v1/mobile/shifts/{shiftId}/rate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

overall_score   integer     

Example: 1

punctuality_score   integer  optional    

Example: 1

professionalism_score   integer  optional    

Example: 1

communication_score   integer  optional    

Example: 1

work_environment_score   integer  optional    

Example: 1

comment   string  optional    

Example: Beispieltext

is_anonymous   boolean  optional    

Example: true

Ratings

List Received Ratings

requires authentication

List the ratings a worker has received from companies.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/ratings/received" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/ratings/received"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/ratings/received';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/ratings/received');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "average_overall": null,
        "count": 0,
        "ratings": []
    }
}
 

Request      

GET api/v1/mobile/ratings/received

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Notifications

Register (or refresh) the authenticated user's push-notification device token.

requires authentication

The Firebase Cloud Messaging token is unique per device install; re-posting an existing token re-binds it to the current user (e.g. after an account switch) and refreshes its platform/name. Idempotent by token.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/devices/register" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"token\": \"fADc...9x\",
    \"platform\": \"ios\",
    \"device_name\": \"iPhone 15 Pro\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/devices/register"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "fADc...9x",
    "platform": "ios",
    "device_name": "iPhone 15 Pro"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/devices/register';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'token' => 'fADc...9x',
            'platform' => 'ios',
            'device_name' => 'iPhone 15 Pro',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/devices/register');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "token": "fADc...9x",
    "platform": "ios",
    "device_name": "iPhone 15 Pro"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Device registered for push notifications.",
    "data": {
        "id": "019fd2b8-3240-70a0-be56-989736211eff",
        "platform": "ios"
    }
}
 

Request      

POST api/v1/mobile/devices/register

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The FCM/APNs device token. Example: fADc...9x

platform   string     

One of ios, android, web. Example: ios

device_name   string  optional    

optional A human-readable device label. Example: iPhone 15 Pro

Unregister a push-notification device token

requires authentication

Removes the token so this device stops receiving notifications for the authenticated user. Idempotent: unregistering a token that is already gone, or one belonging to someone else, reports success without touching another account's registration.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/devices/unregister" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"token\": \"fADc...9x\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/devices/unregister"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "fADc...9x"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/devices/unregister';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'token' => 'fADc...9x',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/devices/unregister');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "token": "fADc...9x"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Device unregistered from push notifications.",
    "data": {
        "removed": false
    }
}
 

Request      

POST api/v1/mobile/devices/unregister

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The FCM/APNs device token to drop. Example: fADc...9x

List Notifications

requires authentication

Paginated list of notifications for the authenticated user. Includes an unread_count in meta for the inbox badge.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/notifications?page=16&per_page=16&unread_only=" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/notifications"
);

const params = {
    "page": "16",
    "per_page": "16",
    "unread_only": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/notifications';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'page' => '16',
            'per_page' => '16',
            'unread_only' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/notifications')
      .replace(queryParameters: {
        'page': '16',
        'per_page': '16',
        'unread_only': '',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Notifications retrieved successfully.",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "c7eb899a-5227-4d81-bdbb-845ed39a9963",
        "timestamp": "2026-08-05T16:18:36.098962Z",
        "current_page": 16,
        "per_page": 16,
        "total": 0,
        "last_page": 1,
        "unread_count": 0
    }
}
 

Request      

GET api/v1/mobile/notifications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

page   integer  optional    

Defaults to 1. Example: 16

per_page   integer  optional    

Defaults to 20 (max 100). Example: 16

unread_only   boolean  optional    

nullable When true, returns only unread notifications. Example: false

Get Unread Notification Count

requires authentication

Returns the count of unread notifications for badge display.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/notifications/unread-count" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/notifications/unread-count"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/notifications/unread-count';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/notifications/unread-count');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "unread_count": 0
    },
    "errors": null,
    "meta": {
        "request_id": "d37f478c-4c14-45ab-ac14-23580ddfa706",
        "timestamp": "2026-08-05T16:18:36.105987Z"
    }
}
 

Request      

GET api/v1/mobile/notifications/unread-count

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Mark Notification Read

requires authentication

Marks a single notification as read.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/notifications/550e8400-e29b-41d4-a716-446655440000/read" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/notifications/550e8400-e29b-41d4-a716-446655440000/read"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/notifications/550e8400-e29b-41d4-a716-446655440000/read';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/notifications/550e8400-e29b-41d4-a716-446655440000/read');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Notification marked as read.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-10T12:00:00.000000Z"
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Notification not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Notification not found."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-10T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/notifications/{notification}/read

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

notification   string     

UUID of the notification. Example: 550e8400-e29b-41d4-a716-446655440000

Mark All Notifications Read

requires authentication

Marks all unread notifications as read.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/notifications/read-all" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/notifications/read-all"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/notifications/read-all';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/notifications/read-all');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "All notifications have been marked as read.",
    "data": {
        "marked_count": 0
    },
    "errors": null,
    "meta": {
        "request_id": "431882ec-4bb8-4d4c-9b72-39376ca1eaa1",
        "timestamp": "2026-08-05T16:18:36.115293Z"
    }
}
 

Request      

POST api/v1/mobile/notifications/read-all

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Returns URLs and versions for all legal documents.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/legal/documents" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/legal/documents"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/legal/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/legal/documents');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "privacy_policy": {
            "key": "privacy_policy",
            "url": "http://localhost:8001/legal/privacy_policy",
            "version": "1",
            "title": "Datenschutzerklärung",
            "required": true
        },
        "terms_of_service": {
            "key": "terms_of_service",
            "url": "http://localhost:8001/legal/terms_of_service",
            "version": "1",
            "title": "Allgemeine Geschäftsbedingungen (AGB)",
            "required": true
        },
        "employment_terms": {
            "key": "employment_terms",
            "url": "http://localhost:8001/legal/aug_info",
            "version": "1",
            "title": "Information gemäß AÜG (Arbeitskräfteüberlassungsgesetz)",
            "required": true
        },
        "cookie_policy": {
            "key": "cookie_policy",
            "url": "http://localhost:8001/legal/cookie_policy",
            "version": "1",
            "title": "Cookie-Richtlinie",
            "required": false
        },
        "data_processing_agreement": {
            "key": "dpa",
            "url": "http://localhost:8001/legal/dpa",
            "version": "2026-06-01",
            "title": "Data Processing Agreement",
            "required": false
        }
    },
    "errors": null,
    "meta": {
        "request_id": "e90e4da8-5533-4b6a-8c64-eee968de3e53",
        "timestamp": "2026-08-05T16:18:34.398969Z"
    }
}
 

Employee Profile

Reference Data

The permitted values for the choices on the profile screens: genders, residence bases, document types and countries. Labels are German, which is the language of the documents these describe.

Safe to cache for a day — these change only when the platform does.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/reference-data" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/reference-data"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/reference-data';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/reference-data');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "genders": [
            {
                "value": "male",
                "label": "Männlich"
            },
            {
                "value": "female",
                "label": "Weiblich"
            },
            {
                "value": "diverse",
                "label": "Divers"
            },
            {
                "value": "not_specified",
                "label": "Keine Angabe"
            }
        ],
        "residence_statuses": [
            {
                "value": "AT_CITIZEN",
                "label": "Österreichische Staatsbürgerschaft"
            },
            {
                "value": "EU_EEA",
                "label": "EU/EWR-Bürger"
            },
            {
                "value": "THIRD_COUNTRY",
                "label": "Drittstaatsangehörige(r)"
            }
        ],
        "document_types": [
            {
                "value": "passport",
                "label": "Passport",
                "requires_expiry": true,
                "is_work_authorisation": false
            },
            {
                "value": "id_card",
                "label": "ID card",
                "requires_expiry": true,
                "is_work_authorisation": false
            },
            {
                "value": "driver_license",
                "label": "Driver's licence",
                "requires_expiry": true,
                "is_work_authorisation": false
            },
            {
                "value": "work_permit",
                "label": "Work permit",
                "requires_expiry": true,
                "is_work_authorisation": true
            },
            {
                "value": "residence_permit",
                "label": "Residence permit",
                "requires_expiry": true,
                "is_work_authorisation": true
            },
            {
                "value": "rot_weiss_rot_karte",
                "label": "Rot-Weiß-Rot card",
                "requires_expiry": true,
                "is_work_authorisation": true
            },
            {
                "value": "blue_card",
                "label": "EU Blue Card",
                "requires_expiry": true,
                "is_work_authorisation": true
            },
            {
                "value": "bank_statement",
                "label": "Bank statement",
                "requires_expiry": false,
                "is_work_authorisation": false
            },
            {
                "value": "proof_of_address",
                "label": "Proof of address",
                "requires_expiry": false,
                "is_work_authorisation": false
            },
            {
                "value": "social_insurance_card",
                "label": "Social insurance record",
                "requires_expiry": false,
                "is_work_authorisation": false
            },
            {
                "value": "tax_document",
                "label": "Tax document",
                "requires_expiry": false,
                "is_work_authorisation": false
            },
            {
                "value": "certification",
                "label": "Certification",
                "requires_expiry": true,
                "is_work_authorisation": false
            },
            {
                "value": "health_certificate",
                "label": "Health certificate",
                "requires_expiry": true,
                "is_work_authorisation": false
            },
            {
                "value": "criminal_record_check",
                "label": "Criminal record check",
                "requires_expiry": false,
                "is_work_authorisation": false
            },
            {
                "value": "other",
                "label": "Other",
                "requires_expiry": false,
                "is_work_authorisation": false
            }
        ],
        "countries": [
            {
                "code": "AF",
                "name": "Afghanistan",
                "dial_code": "+93"
            },
            {
                "code": "AL",
                "name": "Albania",
                "dial_code": "+355"
            },
            {
                "code": "DZ",
                "name": "Algeria",
                "dial_code": "+213"
            },
            {
                "code": "AS",
                "name": "American Samoa",
                "dial_code": "+1"
            },
            {
                "code": "AD",
                "name": "Andorra",
                "dial_code": "+376"
            },
            {
                "code": "AO",
                "name": "Angola",
                "dial_code": "+244"
            },
            {
                "code": "AI",
                "name": "Anguilla",
                "dial_code": "+1"
            },
            {
                "code": "AQ",
                "name": "Antarctica",
                "dial_code": null
            },
            {
                "code": "AG",
                "name": "Antigua and Barbuda",
                "dial_code": "+1"
            },
            {
                "code": "AR",
                "name": "Argentina",
                "dial_code": "+54"
            },
            {
                "code": "AM",
                "name": "Armenia",
                "dial_code": "+374"
            },
            {
                "code": "AW",
                "name": "Aruba",
                "dial_code": "+297"
            },
            {
                "code": "AU",
                "name": "Australia",
                "dial_code": "+61"
            },
            {
                "code": "AT",
                "name": "Austria",
                "dial_code": "+43"
            },
            {
                "code": "AZ",
                "name": "Azerbaijan",
                "dial_code": "+994"
            },
            {
                "code": "BS",
                "name": "Bahamas",
                "dial_code": "+1"
            },
            {
                "code": "BH",
                "name": "Bahrain",
                "dial_code": "+973"
            },
            {
                "code": "BD",
                "name": "Bangladesh",
                "dial_code": "+880"
            },
            {
                "code": "BB",
                "name": "Barbados",
                "dial_code": "+1"
            },
            {
                "code": "BY",
                "name": "Belarus",
                "dial_code": "+375"
            },
            {
                "code": "BE",
                "name": "Belgium",
                "dial_code": "+32"
            },
            {
                "code": "BZ",
                "name": "Belize",
                "dial_code": "+501"
            },
            {
                "code": "BJ",
                "name": "Benin",
                "dial_code": "+229"
            },
            {
                "code": "BM",
                "name": "Bermuda",
                "dial_code": "+1"
            },
            {
                "code": "BT",
                "name": "Bhutan",
                "dial_code": "+975"
            },
            {
                "code": "BO",
                "name": "Bolivia",
                "dial_code": "+591"
            },
            {
                "code": "BQ",
                "name": "Bonaire, Sint Eustatius and Saba",
                "dial_code": "+599"
            },
            {
                "code": "BA",
                "name": "Bosnia and Herzegovina",
                "dial_code": "+387"
            },
            {
                "code": "BW",
                "name": "Botswana",
                "dial_code": "+267"
            },
            {
                "code": "BV",
                "name": "Bouvet Island",
                "dial_code": null
            },
            {
                "code": "BR",
                "name": "Brazil",
                "dial_code": "+55"
            },
            {
                "code": "IO",
                "name": "British Indian Ocean Territory",
                "dial_code": "+246"
            },
            {
                "code": "VG",
                "name": "British Virgin Islands",
                "dial_code": "+1"
            },
            {
                "code": "BN",
                "name": "Brunei",
                "dial_code": "+673"
            },
            {
                "code": "BG",
                "name": "Bulgaria",
                "dial_code": "+359"
            },
            {
                "code": "BF",
                "name": "Burkina Faso",
                "dial_code": "+226"
            },
            {
                "code": "BI",
                "name": "Burundi",
                "dial_code": "+257"
            },
            {
                "code": "CV",
                "name": "Cabo Verde",
                "dial_code": "+238"
            },
            {
                "code": "KH",
                "name": "Cambodia",
                "dial_code": "+855"
            },
            {
                "code": "CM",
                "name": "Cameroon",
                "dial_code": "+237"
            },
            {
                "code": "CA",
                "name": "Canada",
                "dial_code": "+1"
            },
            {
                "code": "KY",
                "name": "Cayman Islands",
                "dial_code": "+1"
            },
            {
                "code": "CF",
                "name": "Central African Republic",
                "dial_code": "+236"
            },
            {
                "code": "TD",
                "name": "Chad",
                "dial_code": "+235"
            },
            {
                "code": "CL",
                "name": "Chile",
                "dial_code": "+56"
            },
            {
                "code": "CN",
                "name": "China",
                "dial_code": "+86"
            },
            {
                "code": "CX",
                "name": "Christmas Island",
                "dial_code": "+61"
            },
            {
                "code": "CC",
                "name": "Cocos (Keeling) Islands",
                "dial_code": "+61"
            },
            {
                "code": "CO",
                "name": "Colombia",
                "dial_code": "+57"
            },
            {
                "code": "KM",
                "name": "Comoros",
                "dial_code": "+269"
            },
            {
                "code": "CD",
                "name": "Congo, Democratic Republic",
                "dial_code": "+243"
            },
            {
                "code": "CG",
                "name": "Congo, Republic",
                "dial_code": "+242"
            },
            {
                "code": "CK",
                "name": "Cook Islands",
                "dial_code": "+682"
            },
            {
                "code": "CR",
                "name": "Costa Rica",
                "dial_code": "+506"
            },
            {
                "code": "HR",
                "name": "Croatia",
                "dial_code": "+385"
            },
            {
                "code": "CU",
                "name": "Cuba",
                "dial_code": "+53"
            },
            {
                "code": "CW",
                "name": "Curaçao",
                "dial_code": "+599"
            },
            {
                "code": "CY",
                "name": "Cyprus",
                "dial_code": "+357"
            },
            {
                "code": "CZ",
                "name": "Czech Republic",
                "dial_code": "+420"
            },
            {
                "code": "CI",
                "name": "Côte d'Ivoire",
                "dial_code": "+225"
            },
            {
                "code": "DK",
                "name": "Denmark",
                "dial_code": "+45"
            },
            {
                "code": "DJ",
                "name": "Djibouti",
                "dial_code": "+253"
            },
            {
                "code": "DM",
                "name": "Dominica",
                "dial_code": "+1"
            },
            {
                "code": "DO",
                "name": "Dominican Republic",
                "dial_code": "+1"
            },
            {
                "code": "EC",
                "name": "Ecuador",
                "dial_code": "+593"
            },
            {
                "code": "EG",
                "name": "Egypt",
                "dial_code": "+20"
            },
            {
                "code": "SV",
                "name": "El Salvador",
                "dial_code": "+503"
            },
            {
                "code": "GQ",
                "name": "Equatorial Guinea",
                "dial_code": "+240"
            },
            {
                "code": "ER",
                "name": "Eritrea",
                "dial_code": "+291"
            },
            {
                "code": "EE",
                "name": "Estonia",
                "dial_code": "+372"
            },
            {
                "code": "SZ",
                "name": "Eswatini",
                "dial_code": "+268"
            },
            {
                "code": "ET",
                "name": "Ethiopia",
                "dial_code": "+251"
            },
            {
                "code": "FK",
                "name": "Falkland Islands",
                "dial_code": "+500"
            },
            {
                "code": "FO",
                "name": "Faroe Islands",
                "dial_code": "+298"
            },
            {
                "code": "FJ",
                "name": "Fiji",
                "dial_code": "+679"
            },
            {
                "code": "FI",
                "name": "Finland",
                "dial_code": "+358"
            },
            {
                "code": "FR",
                "name": "France",
                "dial_code": "+33"
            },
            {
                "code": "GF",
                "name": "French Guiana",
                "dial_code": "+594"
            },
            {
                "code": "PF",
                "name": "French Polynesia",
                "dial_code": "+689"
            },
            {
                "code": "TF",
                "name": "French Southern Territories",
                "dial_code": null
            },
            {
                "code": "GA",
                "name": "Gabon",
                "dial_code": "+241"
            },
            {
                "code": "GM",
                "name": "Gambia",
                "dial_code": "+220"
            },
            {
                "code": "GE",
                "name": "Georgia",
                "dial_code": "+995"
            },
            {
                "code": "DE",
                "name": "Germany",
                "dial_code": "+49"
            },
            {
                "code": "GH",
                "name": "Ghana",
                "dial_code": "+233"
            },
            {
                "code": "GI",
                "name": "Gibraltar",
                "dial_code": "+350"
            },
            {
                "code": "GR",
                "name": "Greece",
                "dial_code": "+30"
            },
            {
                "code": "GL",
                "name": "Greenland",
                "dial_code": "+299"
            },
            {
                "code": "GD",
                "name": "Grenada",
                "dial_code": "+1"
            },
            {
                "code": "GP",
                "name": "Guadeloupe",
                "dial_code": "+590"
            },
            {
                "code": "GU",
                "name": "Guam",
                "dial_code": "+1"
            },
            {
                "code": "GT",
                "name": "Guatemala",
                "dial_code": "+502"
            },
            {
                "code": "GG",
                "name": "Guernsey",
                "dial_code": "+44"
            },
            {
                "code": "GN",
                "name": "Guinea",
                "dial_code": "+224"
            },
            {
                "code": "GW",
                "name": "Guinea-Bissau",
                "dial_code": "+245"
            },
            {
                "code": "GY",
                "name": "Guyana",
                "dial_code": "+592"
            },
            {
                "code": "HT",
                "name": "Haiti",
                "dial_code": "+509"
            },
            {
                "code": "HM",
                "name": "Heard Island and McDonald Islands",
                "dial_code": null
            },
            {
                "code": "HN",
                "name": "Honduras",
                "dial_code": "+504"
            },
            {
                "code": "HK",
                "name": "Hong Kong",
                "dial_code": "+852"
            },
            {
                "code": "HU",
                "name": "Hungary",
                "dial_code": "+36"
            },
            {
                "code": "IS",
                "name": "Iceland",
                "dial_code": "+354"
            },
            {
                "code": "IN",
                "name": "India",
                "dial_code": "+91"
            },
            {
                "code": "ID",
                "name": "Indonesia",
                "dial_code": "+62"
            },
            {
                "code": "IR",
                "name": "Iran",
                "dial_code": "+98"
            },
            {
                "code": "IQ",
                "name": "Iraq",
                "dial_code": "+964"
            },
            {
                "code": "IE",
                "name": "Ireland",
                "dial_code": "+353"
            },
            {
                "code": "IM",
                "name": "Isle of Man",
                "dial_code": "+44"
            },
            {
                "code": "IL",
                "name": "Israel",
                "dial_code": "+972"
            },
            {
                "code": "IT",
                "name": "Italy",
                "dial_code": "+39"
            },
            {
                "code": "JM",
                "name": "Jamaica",
                "dial_code": "+1"
            },
            {
                "code": "JP",
                "name": "Japan",
                "dial_code": "+81"
            },
            {
                "code": "JE",
                "name": "Jersey",
                "dial_code": "+44"
            },
            {
                "code": "JO",
                "name": "Jordan",
                "dial_code": "+962"
            },
            {
                "code": "KZ",
                "name": "Kazakhstan",
                "dial_code": "+7"
            },
            {
                "code": "KE",
                "name": "Kenya",
                "dial_code": "+254"
            },
            {
                "code": "KI",
                "name": "Kiribati",
                "dial_code": "+686"
            },
            {
                "code": "XK",
                "name": "Kosovo",
                "dial_code": "+383"
            },
            {
                "code": "KW",
                "name": "Kuwait",
                "dial_code": "+965"
            },
            {
                "code": "KG",
                "name": "Kyrgyzstan",
                "dial_code": "+996"
            },
            {
                "code": "LA",
                "name": "Laos",
                "dial_code": "+856"
            },
            {
                "code": "LV",
                "name": "Latvia",
                "dial_code": "+371"
            },
            {
                "code": "LB",
                "name": "Lebanon",
                "dial_code": "+961"
            },
            {
                "code": "LS",
                "name": "Lesotho",
                "dial_code": "+266"
            },
            {
                "code": "LR",
                "name": "Liberia",
                "dial_code": "+231"
            },
            {
                "code": "LY",
                "name": "Libya",
                "dial_code": "+218"
            },
            {
                "code": "LI",
                "name": "Liechtenstein",
                "dial_code": "+423"
            },
            {
                "code": "LT",
                "name": "Lithuania",
                "dial_code": "+370"
            },
            {
                "code": "LU",
                "name": "Luxembourg",
                "dial_code": "+352"
            },
            {
                "code": "MO",
                "name": "Macao",
                "dial_code": "+853"
            },
            {
                "code": "MG",
                "name": "Madagascar",
                "dial_code": "+261"
            },
            {
                "code": "MW",
                "name": "Malawi",
                "dial_code": "+265"
            },
            {
                "code": "MY",
                "name": "Malaysia",
                "dial_code": "+60"
            },
            {
                "code": "MV",
                "name": "Maldives",
                "dial_code": "+960"
            },
            {
                "code": "ML",
                "name": "Mali",
                "dial_code": "+223"
            },
            {
                "code": "MT",
                "name": "Malta",
                "dial_code": "+356"
            },
            {
                "code": "MH",
                "name": "Marshall Islands",
                "dial_code": "+692"
            },
            {
                "code": "MQ",
                "name": "Martinique",
                "dial_code": "+596"
            },
            {
                "code": "MR",
                "name": "Mauritania",
                "dial_code": "+222"
            },
            {
                "code": "MU",
                "name": "Mauritius",
                "dial_code": "+230"
            },
            {
                "code": "YT",
                "name": "Mayotte",
                "dial_code": "+262"
            },
            {
                "code": "MX",
                "name": "Mexico",
                "dial_code": "+52"
            },
            {
                "code": "FM",
                "name": "Micronesia",
                "dial_code": "+691"
            },
            {
                "code": "MD",
                "name": "Moldova",
                "dial_code": "+373"
            },
            {
                "code": "MC",
                "name": "Monaco",
                "dial_code": "+377"
            },
            {
                "code": "MN",
                "name": "Mongolia",
                "dial_code": "+976"
            },
            {
                "code": "ME",
                "name": "Montenegro",
                "dial_code": "+382"
            },
            {
                "code": "MS",
                "name": "Montserrat",
                "dial_code": "+1"
            },
            {
                "code": "MA",
                "name": "Morocco",
                "dial_code": "+212"
            },
            {
                "code": "MZ",
                "name": "Mozambique",
                "dial_code": "+258"
            },
            {
                "code": "MM",
                "name": "Myanmar",
                "dial_code": "+95"
            },
            {
                "code": "NA",
                "name": "Namibia",
                "dial_code": "+264"
            },
            {
                "code": "NR",
                "name": "Nauru",
                "dial_code": "+674"
            },
            {
                "code": "NP",
                "name": "Nepal",
                "dial_code": "+977"
            },
            {
                "code": "NL",
                "name": "Netherlands",
                "dial_code": "+31"
            },
            {
                "code": "NC",
                "name": "New Caledonia",
                "dial_code": "+687"
            },
            {
                "code": "NZ",
                "name": "New Zealand",
                "dial_code": "+64"
            },
            {
                "code": "NI",
                "name": "Nicaragua",
                "dial_code": "+505"
            },
            {
                "code": "NE",
                "name": "Niger",
                "dial_code": "+227"
            },
            {
                "code": "NG",
                "name": "Nigeria",
                "dial_code": "+234"
            },
            {
                "code": "NU",
                "name": "Niue",
                "dial_code": "+683"
            },
            {
                "code": "NF",
                "name": "Norfolk Island",
                "dial_code": "+672"
            },
            {
                "code": "KP",
                "name": "North Korea",
                "dial_code": "+850"
            },
            {
                "code": "MK",
                "name": "North Macedonia",
                "dial_code": "+389"
            },
            {
                "code": "MP",
                "name": "Northern Mariana Islands",
                "dial_code": "+1"
            },
            {
                "code": "NO",
                "name": "Norway",
                "dial_code": "+47"
            },
            {
                "code": "OM",
                "name": "Oman",
                "dial_code": "+968"
            },
            {
                "code": "PK",
                "name": "Pakistan",
                "dial_code": "+92"
            },
            {
                "code": "PW",
                "name": "Palau",
                "dial_code": "+680"
            },
            {
                "code": "PS",
                "name": "Palestine",
                "dial_code": "+970"
            },
            {
                "code": "PA",
                "name": "Panama",
                "dial_code": "+507"
            },
            {
                "code": "PG",
                "name": "Papua New Guinea",
                "dial_code": "+675"
            },
            {
                "code": "PY",
                "name": "Paraguay",
                "dial_code": "+595"
            },
            {
                "code": "PE",
                "name": "Peru",
                "dial_code": "+51"
            },
            {
                "code": "PH",
                "name": "Philippines",
                "dial_code": "+63"
            },
            {
                "code": "PN",
                "name": "Pitcairn Islands",
                "dial_code": "+64"
            },
            {
                "code": "PL",
                "name": "Poland",
                "dial_code": "+48"
            },
            {
                "code": "PT",
                "name": "Portugal",
                "dial_code": "+351"
            },
            {
                "code": "PR",
                "name": "Puerto Rico",
                "dial_code": "+1"
            },
            {
                "code": "QA",
                "name": "Qatar",
                "dial_code": "+974"
            },
            {
                "code": "RO",
                "name": "Romania",
                "dial_code": "+40"
            },
            {
                "code": "RU",
                "name": "Russia",
                "dial_code": "+7"
            },
            {
                "code": "RW",
                "name": "Rwanda",
                "dial_code": "+250"
            },
            {
                "code": "RE",
                "name": "Réunion",
                "dial_code": "+262"
            },
            {
                "code": "BL",
                "name": "Saint Barthélemy",
                "dial_code": "+590"
            },
            {
                "code": "SH",
                "name": "Saint Helena",
                "dial_code": "+290"
            },
            {
                "code": "KN",
                "name": "Saint Kitts and Nevis",
                "dial_code": "+1"
            },
            {
                "code": "LC",
                "name": "Saint Lucia",
                "dial_code": "+1"
            },
            {
                "code": "MF",
                "name": "Saint Martin",
                "dial_code": "+590"
            },
            {
                "code": "PM",
                "name": "Saint Pierre and Miquelon",
                "dial_code": "+508"
            },
            {
                "code": "VC",
                "name": "Saint Vincent and the Grenadines",
                "dial_code": "+1"
            },
            {
                "code": "WS",
                "name": "Samoa",
                "dial_code": "+685"
            },
            {
                "code": "SM",
                "name": "San Marino",
                "dial_code": "+378"
            },
            {
                "code": "SA",
                "name": "Saudi Arabia",
                "dial_code": "+966"
            },
            {
                "code": "SN",
                "name": "Senegal",
                "dial_code": "+221"
            },
            {
                "code": "RS",
                "name": "Serbia",
                "dial_code": "+381"
            },
            {
                "code": "SC",
                "name": "Seychelles",
                "dial_code": "+248"
            },
            {
                "code": "SL",
                "name": "Sierra Leone",
                "dial_code": "+232"
            },
            {
                "code": "SG",
                "name": "Singapore",
                "dial_code": "+65"
            },
            {
                "code": "SX",
                "name": "Sint Maarten",
                "dial_code": "+1"
            },
            {
                "code": "SK",
                "name": "Slovakia",
                "dial_code": "+421"
            },
            {
                "code": "SI",
                "name": "Slovenia",
                "dial_code": "+386"
            },
            {
                "code": "SB",
                "name": "Solomon Islands",
                "dial_code": "+677"
            },
            {
                "code": "SO",
                "name": "Somalia",
                "dial_code": "+252"
            },
            {
                "code": "ZA",
                "name": "South Africa",
                "dial_code": "+27"
            },
            {
                "code": "GS",
                "name": "South Georgia and South Sandwich",
                "dial_code": null
            },
            {
                "code": "KR",
                "name": "South Korea",
                "dial_code": "+82"
            },
            {
                "code": "SS",
                "name": "South Sudan",
                "dial_code": "+211"
            },
            {
                "code": "ES",
                "name": "Spain",
                "dial_code": "+34"
            },
            {
                "code": "LK",
                "name": "Sri Lanka",
                "dial_code": "+94"
            },
            {
                "code": "SD",
                "name": "Sudan",
                "dial_code": "+249"
            },
            {
                "code": "SR",
                "name": "Suriname",
                "dial_code": "+597"
            },
            {
                "code": "SJ",
                "name": "Svalbard and Jan Mayen",
                "dial_code": "+47"
            },
            {
                "code": "SE",
                "name": "Sweden",
                "dial_code": "+46"
            },
            {
                "code": "CH",
                "name": "Switzerland",
                "dial_code": "+41"
            },
            {
                "code": "SY",
                "name": "Syria",
                "dial_code": "+963"
            },
            {
                "code": "ST",
                "name": "São Tomé and Príncipe",
                "dial_code": "+239"
            },
            {
                "code": "TW",
                "name": "Taiwan",
                "dial_code": "+886"
            },
            {
                "code": "TJ",
                "name": "Tajikistan",
                "dial_code": "+992"
            },
            {
                "code": "TZ",
                "name": "Tanzania",
                "dial_code": "+255"
            },
            {
                "code": "TH",
                "name": "Thailand",
                "dial_code": "+66"
            },
            {
                "code": "TL",
                "name": "Timor-Leste",
                "dial_code": "+670"
            },
            {
                "code": "TG",
                "name": "Togo",
                "dial_code": "+228"
            },
            {
                "code": "TK",
                "name": "Tokelau",
                "dial_code": "+690"
            },
            {
                "code": "TO",
                "name": "Tonga",
                "dial_code": "+676"
            },
            {
                "code": "TT",
                "name": "Trinidad and Tobago",
                "dial_code": "+1"
            },
            {
                "code": "TN",
                "name": "Tunisia",
                "dial_code": "+216"
            },
            {
                "code": "TR",
                "name": "Turkey",
                "dial_code": "+90"
            },
            {
                "code": "TM",
                "name": "Turkmenistan",
                "dial_code": "+993"
            },
            {
                "code": "TC",
                "name": "Turks and Caicos Islands",
                "dial_code": "+1"
            },
            {
                "code": "TV",
                "name": "Tuvalu",
                "dial_code": "+688"
            },
            {
                "code": "UM",
                "name": "U.S. Minor Outlying Islands",
                "dial_code": "+1"
            },
            {
                "code": "VI",
                "name": "U.S. Virgin Islands",
                "dial_code": "+1"
            },
            {
                "code": "UG",
                "name": "Uganda",
                "dial_code": "+256"
            },
            {
                "code": "UA",
                "name": "Ukraine",
                "dial_code": "+380"
            },
            {
                "code": "AE",
                "name": "United Arab Emirates",
                "dial_code": "+971"
            },
            {
                "code": "GB",
                "name": "United Kingdom",
                "dial_code": "+44"
            },
            {
                "code": "US",
                "name": "United States",
                "dial_code": "+1"
            },
            {
                "code": "UY",
                "name": "Uruguay",
                "dial_code": "+598"
            },
            {
                "code": "UZ",
                "name": "Uzbekistan",
                "dial_code": "+998"
            },
            {
                "code": "VU",
                "name": "Vanuatu",
                "dial_code": "+678"
            },
            {
                "code": "VA",
                "name": "Vatican City",
                "dial_code": "+39"
            },
            {
                "code": "VE",
                "name": "Venezuela",
                "dial_code": "+58"
            },
            {
                "code": "VN",
                "name": "Vietnam",
                "dial_code": "+84"
            },
            {
                "code": "WF",
                "name": "Wallis and Futuna",
                "dial_code": "+681"
            },
            {
                "code": "EH",
                "name": "Western Sahara",
                "dial_code": "+212"
            },
            {
                "code": "YE",
                "name": "Yemen",
                "dial_code": "+967"
            },
            {
                "code": "ZM",
                "name": "Zambia",
                "dial_code": "+260"
            },
            {
                "code": "ZW",
                "name": "Zimbabwe",
                "dial_code": "+263"
            },
            {
                "code": "AX",
                "name": "Åland Islands",
                "dial_code": "+358"
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/reference-data

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Profile Completion

requires authentication

What the onboarding UI should drive off: an overall percentage plus the state of each section, so the app can show exactly what is outstanding rather than guessing from the profile payload.

A profile is complete only when every required section is — personal, address, tax, bank and documents. The emergency contact is collected but never required. Once all of them are satisfied and the email is verified, the profile moves itself to pending_approval and the framework agreement is minted for signing; an admin approval is what finally makes it active.

correction_fields is populated when a reviewer sent the profile back, and names the fields they want changed.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/completion" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/completion"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/completion';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/completion');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "completion_percentage": 50,
        "sections": [
            {
                "key": "personal",
                "complete": false,
                "missing_fields": [
                    "legal_first_name",
                    "legal_last_name",
                    "gender",
                    "citizenship_primary",
                    "residence_status"
                ],
                "required": true
            },
            {
                "key": "contact",
                "complete": true,
                "missing_fields": [],
                "required": true
            },
            {
                "key": "address",
                "complete": true,
                "missing_fields": [],
                "required": true
            },
            {
                "key": "tax",
                "complete": true,
                "missing_fields": [],
                "required": true
            },
            {
                "key": "bank",
                "complete": false,
                "missing_fields": [
                    "bank_account"
                ],
                "required": true
            },
            {
                "key": "documents",
                "complete": false,
                "missing_fields": [
                    "address_proof",
                    "social_insurance",
                    "bank"
                ],
                "missing_documents": [
                    {
                        "key": "address_proof",
                        "label": "Meldezettel",
                        "accepts": [
                            "proof_of_address"
                        ]
                    },
                    {
                        "key": "social_insurance",
                        "label": "e-card (Sozialversicherung)",
                        "accepts": [
                            "social_insurance_card"
                        ]
                    },
                    {
                        "key": "bank",
                        "label": "Kontoauszug",
                        "accepts": [
                            "bank_statement"
                        ]
                    }
                ],
                "required": true
            },
            {
                "key": "emergency",
                "complete": false,
                "missing_fields": [
                    "emergency_contact_name",
                    "emergency_contact_phone"
                ],
                "required": false
            }
        ],
        "correction_fields": null
    },
    "errors": null,
    "meta": {
        "request_id": "d4f66e1d-1c44-4c19-94d8-3bc9146f913f",
        "timestamp": "2026-08-05T16:18:35.700301Z"
    }
}
 

Request      

GET api/v1/mobile/employee-profile/completion

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Personal Details

requires authentication

Identity as it must appear on the employment contract, so the values here are matched against the uploaded ID during review — a mismatch sends the profile back for correction rather than failing silently.

Every field is optional on its own: the section is saved incrementally as the worker fills the wizard, and completion is judged separately by GET /employee-profile/completion.

Saving this while the profile is pending_approval, rejected or archived is refused — a record under review cannot move beneath the reviewer.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/personal" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"legal_first_name\": \"Jonas\",
    \"legal_last_name\": \"Brunner\",
    \"middle_names\": \"Maria\",
    \"previous_last_names\": \"Huber\",
    \"date_of_birth\": \"1995-04-12\",
    \"place_of_birth\": \"Graz\",
    \"country_of_birth\": \"AT\",
    \"gender\": \"M\",
    \"citizenship_primary\": \"AT\",
    \"citizenship_additional\": [
        \"DE\"
    ],
    \"residence_status\": \"AT_CITIZEN\",
    \"has_driver_license\": true,
    \"has_car\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/personal"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "legal_first_name": "Jonas",
    "legal_last_name": "Brunner",
    "middle_names": "Maria",
    "previous_last_names": "Huber",
    "date_of_birth": "1995-04-12",
    "place_of_birth": "Graz",
    "country_of_birth": "AT",
    "gender": "M",
    "citizenship_primary": "AT",
    "citizenship_additional": [
        "DE"
    ],
    "residence_status": "AT_CITIZEN",
    "has_driver_license": true,
    "has_car": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/personal';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'legal_first_name' => 'Jonas',
            'legal_last_name' => 'Brunner',
            'middle_names' => 'Maria',
            'previous_last_names' => 'Huber',
            'date_of_birth' => '1995-04-12',
            'place_of_birth' => 'Graz',
            'country_of_birth' => 'AT',
            'gender' => 'M',
            'citizenship_primary' => 'AT',
            'citizenship_additional' => ['DE'],
            'residence_status' => 'AT_CITIZEN',
            'has_driver_license' => true,
            'has_car' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/personal');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "legal_first_name": "Jonas",
    "legal_last_name": "Brunner",
    "middle_names": "Maria",
    "previous_last_names": "Huber",
    "date_of_birth": "1995-04-12",
    "place_of_birth": "Graz",
    "country_of_birth": "AT",
    "gender": "M",
    "citizenship_primary": "AT",
    "citizenship_additional": [
        "DE"
    ],
    "residence_status": "AT_CITIZEN",
    "has_driver_license": true,
    "has_car": false
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (422, Under 18):


{
    "status": "VALIDATION_ERROR",
    "message": "The date of birth must be at least 18 years ago."
}
 

Request      

PUT api/v1/mobile/employee-profile/personal

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

legal_first_name   string  optional    

Given name exactly as on the ID document. Example: Jonas

legal_last_name   string  optional    

Surname exactly as on the ID document. Example: Brunner

middle_names   string  optional    

Middle names, if the document shows any. Example: Maria

previous_last_names   string  optional    

Former surnames, for matching older documents. Example: Huber

date_of_birth   date  optional    

Must be at least 18 years ago — under-18s cannot be placed. Example: 1995-04-12

place_of_birth   string  optional    

Town of birth. Example: Graz

country_of_birth   string  optional    

Two-letter ISO country code. Example: AT

gender   string  optional    

M, F or D — or the long form male, female, diverse, not_specified. Example: M

citizenship_primary   string  optional    

Two-letter ISO code of the main citizenship. Example: AT

citizenship_additional   string[]  optional    

Further citizenships, each a two-letter ISO code.

residence_status   string  optional    

Residence basis, which decides whether a work permit is also required. Allowed: AT_CITIZEN, EU_EEA, THIRD_COUNTRY. Example: AT_CITIZEN

has_driver_license   boolean  optional    

Whether the worker holds a driving licence. Example: true

has_car   boolean  optional    

Whether the worker has their own vehicle. Example: false

Update Contact Details

requires authentication

Secondary contact details only. The primary email and phone are changed through POST /profile/email/request-change and POST /profile/phone/request-change, which verify ownership first — neither can be overwritten here.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/contact" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"alternate_email\": \"jonas.privat@example.com\",
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/contact"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "alternate_email": "jonas.privat@example.com",
    "country_code": "+43",
    "phone_number": "6641234567"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/contact';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'alternate_email' => 'jonas.privat@example.com',
            'country_code' => '+43',
            'phone_number' => '6641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/contact');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "alternate_email": "jonas.privat@example.com",
    "country_code": "+43",
    "phone_number": "6641234567"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/mobile/employee-profile/contact

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

alternate_email   string  optional    

A secondary address, used only if the primary bounces. Example: jonas.privat@example.com

country_code   string  optional    

Dialling code with leading plus. Example: +43

phone_number   string  optional    

National number without the dialling code. Example: 6641234567

Update Address

requires authentication

The worker's residential address, which the platform treats as their registered (gemeldete) address — the two are assumed to be the same.

Changing the address invalidates any proof-of-address document already on file: it is set back to reupload_requested, and an already-approved profile returns to pending_approval. That is deliberate — the old document no longer evidences where the worker lives.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/address" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"address_line_1\": \"Hauptstrasse\",
    \"house_number\": \"12\",
    \"apartment\": \"Top 4\",
    \"postal_code\": \"1010\",
    \"city\": \"Wien\",
    \"country\": \"AT\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/address"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "address_line_1": "Hauptstrasse",
    "house_number": "12",
    "apartment": "Top 4",
    "postal_code": "1010",
    "city": "Wien",
    "country": "AT"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/address';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'address_line_1' => 'Hauptstrasse',
            'house_number' => '12',
            'apartment' => 'Top 4',
            'postal_code' => '1010',
            'city' => 'Wien',
            'country' => 'AT',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/address');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "address_line_1": "Hauptstrasse",
    "house_number": "12",
    "apartment": "Top 4",
    "postal_code": "1010",
    "city": "Wien",
    "country": "AT"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Profile section updated successfully.",
    "data": {
        "status": "pending_approval",
        "completion_percentage": 50,
        "correction_fields": null
    },
    "errors": null,
    "meta": {
        "request_id": "873b0a88-e989-4e03-b42d-1dbb7f620553",
        "timestamp": "2026-08-05T16:18:35.741770Z"
    }
}
 

Example response (422, Invalid Austrian postal code):


{
    "status": "VALIDATION_ERROR",
    "message": "An Austrian postal code must be four digits."
}
 

Request      

PUT api/v1/mobile/employee-profile/address

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

address_line_1   string  optional    

Street name. Example: Hauptstrasse

house_number   string  optional    

House number. Example: 12

apartment   string  optional    

Stair/door or apartment. Example: Top 4

postal_code   string  optional    

Postal code. Austrian codes must be exactly four digits. Example: 1010

city   string  optional    

City. Example: Wien

country   string  optional    

Two-letter ISO country code. Example: AT

Update Tax and Social Insurance

requires authentication

The Sozialversicherungsnummer is the one field payroll cannot proceed without: it identifies the worker to the Austrian social insurance system and is validated by checksum here, not merely by length.

Three refusals are worth distinguishing, because the app should do something different for each:

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/tax" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"svs_number\": \"1234010195\",
    \"tax_number\": \"12 345\\/6789\",
    \"finanz_online_registered\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/tax"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "svs_number": "1234010195",
    "tax_number": "12 345\/6789",
    "finanz_online_registered": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/tax';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'svs_number' => '1234010195',
            'tax_number' => '12 345/6789',
            'finanz_online_registered' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/tax');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "svs_number": "1234010195",
    "tax_number": "12 345\/6789",
    "finanz_online_registered": false
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (409, Number already held by another profile):


{
    "status": "SVS_NUMBER_ALREADY_REGISTERED",
    "message": "This social-insurance number is already registered.",
    "data": {
        "ticket_category": "profile_change"
    },
    "errors": [
        {
            "field": "svs_number",
            "message": "This social-insurance number is already registered."
        }
    ]
}
 

Example response (422, Checksum failed):


{
    "status": "VALIDATION_ERROR",
    "message": "The social insurance number is not valid."
}
 

Request      

PUT api/v1/mobile/employee-profile/tax

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

svs_number   string  optional    

Ten-digit Sozialversicherungsnummer. The final digits encode the date of birth and the fourth digit is a checksum, both of which are verified. Example: 1234010195

tax_number   string  optional    

Finanzamt tax number in the form "12 345/6789". Example: 12 345/6789

finanz_online_registered   boolean  optional    

Whether the worker is registered with FinanzOnline. Example: false

Update Emergency Contact

requires authentication

Who to call if something happens on site. Collected for every worker but never required for profile completion, so leaving it empty does not hold up approval.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/emergency" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"emergency_contact_name\": \"Maria Brunner\",
    \"emergency_contact_country_code\": \"+43\",
    \"emergency_contact_phone\": \"6641234567\",
    \"emergency_contact_relationship\": \"Schwester\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/emergency"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "emergency_contact_name": "Maria Brunner",
    "emergency_contact_country_code": "+43",
    "emergency_contact_phone": "6641234567",
    "emergency_contact_relationship": "Schwester"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/emergency';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'emergency_contact_name' => 'Maria Brunner',
            'emergency_contact_country_code' => '+43',
            'emergency_contact_phone' => '6641234567',
            'emergency_contact_relationship' => 'Schwester',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/emergency');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "emergency_contact_name": "Maria Brunner",
    "emergency_contact_country_code": "+43",
    "emergency_contact_phone": "6641234567",
    "emergency_contact_relationship": "Schwester"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Profile section updated successfully.",
    "data": {
        "status": "active",
        "completion_percentage": 50,
        "correction_fields": null
    },
    "errors": null,
    "meta": {
        "request_id": "6be59063-d244-479e-bd91-343b9cbd343d",
        "timestamp": "2026-08-05T16:18:35.766541Z"
    }
}
 

Request      

PUT api/v1/mobile/employee-profile/emergency

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

emergency_contact_name   string  optional    

Full name of the contact. Example: Maria Brunner

emergency_contact_country_code   string  optional    

Dialling code including the plus sign. Example: +43

emergency_contact_phone   string  optional    

Number without the dialling code. Example: 6641234567

emergency_contact_relationship   string  optional    

Relationship to the worker. Example: Schwester

List Language Skills

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "languages": []
    },
    "errors": null,
    "meta": {
        "request_id": "526335a2-9a99-4e27-940a-4d597f30c1f5",
        "timestamp": "2026-08-05T16:18:35.774502Z"
    }
}
 

Request      

GET api/v1/mobile/employee-profile/languages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Replace Language Skills

requires authentication

Sends the complete list — whatever is posted becomes the worker's languages, so omitting an entry removes it. Jobs that require a language are matched against these.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"languages\": [
        {
            \"code\": \"de\",
            \"level\": \"c1\"
        }
    ]
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "languages": [
        {
            "code": "de",
            "level": "c1"
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'languages' => [
                ['code' => 'de', 'level' => 'c1'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile/languages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "languages": [
        {
            "code": "de",
            "level": "c1"
        }
    ]
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Saved):


{
    "status": "SUCCESS",
    "data": {
        "languages": [
            {
                "code": "de",
                "level": "c1"
            },
            {
                "code": "en",
                "level": "b2"
            }
        ]
    }
}
 

Request      

PUT api/v1/mobile/employee-profile/languages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

languages   object[]     

The full set, up to 20. Send an empty array to clear them.

code   string     

Language code. Example: de

level   string     

Proficiency, one of the levels the platform recognises. Example: c1

Get Employee Profile

requires authentication

The worker's full employment record — identity, address, tax, bank and document state — as opposed to GET /profile, which is the user account behind the login.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/employee-profile"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/employee-profile';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/employee-profile');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "user": {
            "id": "019fd2b7-f271-73fd-8268-848be381e136",
            "first_name": "Anna",
            "last_name": "Neuling",
            "email": "pending@demo.flexxr.at",
            "phone": "+436769876543",
            "country_code": "+43",
            "phone_number": "6769876543",
            "email_verified": true,
            "phone_verified": true,
            "pending_phone": "+436641239876"
        },
        "profile": {
            "id": "019fd2b7-f279-7142-a68d-0d4563a06070",
            "status": "active",
            "status_label": "Aktiv",
            "completion_percentage": 0,
            "svs_number_set": true,
            "tax_id_set": false,
            "tax_number_set": false,
            "nationality": "AT",
            "date_of_birth": "1995-08-20",
            "place_of_birth": null,
            "gender": null,
            "legal_first_name": null,
            "legal_last_name": null,
            "middle_names": null,
            "previous_last_names": null,
            "country_of_birth": null,
            "residence_status": null,
            "citizenship_primary": null,
            "citizenship_additional": null,
            "has_driver_license": false,
            "has_car": false,
            "finanz_online_registered": false,
            "alternate_email": null,
            "correction_fields": null,
            "address": {
                "line_1": "Neugasse 5",
                "line_2": null,
                "house_number": null,
                "apartment": null,
                "postal_code": "4020",
                "city": "Linz",
                "country": "AT"
            },
            "emergency_contact": {
                "name": null,
                "country_code": null,
                "phone": null,
                "phone_e164": null,
                "relationship": null
            },
            "is_eu_citizen": false,
            "requires_work_permit": false,
            "work_permit_expires_at": null,
            "work_permit_expiring_soon": false,
            "work_permit_expired": false,
            "onboarding_completed_at": "2026-08-05 16:18:32",
            "rejection_reason": null,
            "suspended_reason": null,
            "suspended_until": null
        },
        "consents": {
            "terms_of_service": {
                "accepted": true,
                "version": "1.0",
                "document_id": null,
                "accepted_at": "2026-08-05T16:18:17+00:00"
            },
            "privacy_policy": {
                "accepted": false,
                "version": null,
                "document_id": null,
                "accepted_at": null
            },
            "data_processing": {
                "accepted": false,
                "version": null,
                "document_id": null,
                "accepted_at": null
            },
            "marketing_email": {
                "accepted": false,
                "version": null,
                "document_id": null,
                "accepted_at": null
            }
        },
        "needs_onboarding": false,
        "can_apply_for_jobs": true
    }
}
 

Example response (404, No profile yet):


{
    "status": "NOT_FOUND",
    "message": "Please complete your employee profile."
}
 

Request      

GET api/v1/mobile/employee-profile

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Privacy & GDPR

Download Data Export

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac/download" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac/download"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac/download';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac/download');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "eb800bbb-4c83-4588-b389-7c02b8fdd66d",
        "timestamp": "2026-08-21T05:14:32.739710Z"
    }
}
 

Request      

GET api/v1/mobile/gdpr/export/{export_id}/download

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

export_id   string     

The ID of the export. Example: 019febec-0321-7368-9b4e-d906c6855bac

requires authentication

Returns a paginated list of consent decisions recorded for the authenticated user, including consents captured during registration.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent-history?per_page=25" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent-history"
);

const params = {
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent-history';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '25',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent-history')
      .replace(queryParameters: {
        'per_page': '25',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "1e5faf19-0862-4e67-9563-a92fdc0f1f7c",
        "timestamp": "2026-08-05T16:18:36.169396Z",
        "total": 0,
        "current_page": 1,
        "last_page": 1,
        "per_page": 25
    }
}
 

requires authentication

Records a consent decision for a legal document.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"consent_type\": \"privacy_policy\",
    \"version\": \"2026-06-01\",
    \"is_granted\": true,
    \"client_timestamp\": \"2026-07-26T09:00:00Z\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "consent_type": "privacy_policy",
    "version": "2026-06-01",
    "is_granted": true,
    "client_timestamp": "2026-07-26T09:00:00Z"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'consent_type' => 'privacy_policy',
            'version' => '2026-06-01',
            'is_granted' => true,
            'client_timestamp' => '2026-07-26T09:00:00Z',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/consent');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "consent_type": "privacy_policy",
    "version": "2026-06-01",
    "is_granted": true,
    "client_timestamp": "2026-07-26T09:00:00Z"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your consent has been recorded successfully.",
    "data": {
        "consent_type": "privacy_policy",
        "version": "2026-06-01",
        "is_granted": true,
        "recorded_at": "2026-08-05T16:18:36+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "2672f5c3-f949-4ea6-87f4-f965aef850be",
        "timestamp": "2026-08-05T16:18:36.181527Z"
    }
}
 

Export My Data (Art. 15 DSGVO)

requires authentication

Returns all personal data held about the authenticated user. Rate-limited to once per 5 minutes.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Data export has been generated.",
    "data": {
        "user": {
            "id": "019fd2b7-f271-73fd-8268-848be381e136",
            "first_name": "Anna",
            "last_name": "Neuling",
            "email": "pending@demo.flexxr.at",
            "email_verified_at": "2026-08-05T16:18:32+00:00",
            "phone": "+436769876543",
            "date_of_birth": null,
            "status": "active",
            "notify_push": true,
            "notify_email": true,
            "notify_sms": true,
            "notify_marketing": false,
            "created_at": "2026-08-05T16:18:18+00:00"
        },
        "employee_profile": {
            "status": "active",
            "svs_number": "****0895",
            "tax_id": null,
            "tax_number": null,
            "nationality": "AT",
            "date_of_birth": "1995-08-20",
            "place_of_birth": null,
            "gender": null,
            "legal_first_name": null,
            "legal_last_name": null,
            "middle_names": null,
            "previous_last_names": null,
            "country_of_birth": null,
            "citizenship_primary": null,
            "citizenship_additional": null,
            "residence_status": null,
            "alternate_email": null,
            "address": "Neugasse 5",
            "address_line_2": null,
            "house_number": null,
            "apartment": null,
            "city": "Linz",
            "postal_code": "4020",
            "country": "AT",
            "finanz_online_registered": false,
            "is_eu_citizen": false,
            "requires_work_permit": false,
            "work_permit_expires_at": null,
            "onboarding_completed_at": "2026-08-05T16:18:32+00:00"
        },
        "documents": [
            {
                "type": "id_card",
                "status": "pending",
                "expires_at": null,
                "uploaded_at": "2026-08-05T16:18:18+00:00"
            },
            {
                "type": "passport",
                "status": "verified",
                "expires_at": "2033-05-22",
                "uploaded_at": "2026-08-05T16:18:32+00:00"
            }
        ],
        "attachments": [
            {
                "label": "voluptas nihil",
                "original_filename": "est.pdf",
                "file_size": 293696,
                "uploaded_at": "2026-08-05T16:18:32+00:00"
            }
        ],
        "qualifications": [],
        "bank_accounts": [],
        "applications": [
            {
                "job_title": "Accountant",
                "status": "selected",
                "applied_at": "2026-08-05T16:18:32+00:00"
            }
        ],
        "shifts": [
            {
                "date": null,
                "start_time": null,
                "end_time": null,
                "status": "completed",
                "clock_in_at": null,
                "clock_out_at": null
            },
            {
                "date": null,
                "start_time": null,
                "end_time": null,
                "status": "awaiting_signature",
                "clock_in_at": null,
                "clock_out_at": null
            },
            {
                "date": null,
                "start_time": null,
                "end_time": null,
                "status": "checked_in",
                "clock_in_at": null,
                "clock_out_at": null
            },
            {
                "date": null,
                "start_time": null,
                "end_time": null,
                "status": "signed",
                "clock_in_at": null,
                "clock_out_at": null
            }
        ],
        "contracts": [],
        "ratings_given": [],
        "ratings_received": [],
        "consents": [
            {
                "consent_type": "terms_of_service",
                "version": "1.0",
                "is_granted": true,
                "recorded_at": "2026-08-05T16:18:17+00:00"
            }
        ],
        "exported_at": "2026-08-05T16:18:36+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "aa451d65-823a-4848-850b-ff7a4caf66e2",
        "timestamp": "2026-08-05T16:18:36.201637Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "TOO_MANY_REQUESTS",
    "message": "Please wait before requesting another export.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Please wait 5 minute(s) before requesting another export."
        }
    ],
    "meta": {}
}
 

Request      

GET api/v1/mobile/gdpr/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Request Data Export (Art. 20 DSGVO)

requires authentication

Queues an asynchronous export of all personal data (JSON + copies of uploaded documents) as a ZIP. Poll the returned status URL until the export is ready, then download it within 48 hours.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (202, Recorded from a live call):


{
    "status": "ACCEPTED",
    "message": "Data export started. You will be notified once it is ready.",
    "data": {
        "export_id": "019fd2b8-3830-7193-a147-36d8a971a236",
        "status": "pending",
        "status_url": "http://localhost:8001/api/v1/mobile/gdpr/export/019fd2b8-3830-7193-a147-36d8a971a236",
        "expires_at": "2026-08-07T16:18:36+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "9dc62923-ca91-4a86-a867-3be0ff085aef",
        "timestamp": "2026-08-05T16:18:36.211284Z"
    }
}
 

Request      

POST api/v1/mobile/gdpr/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Data Export Status

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/export/019febec-0321-7368-9b4e-d906c6855bac');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Ready):


{
    "status": "SUCCESS",
    "message": null,
    "data": {
        "export_id": "exp-uuid",
        "status": "ready",
        "download_url": "https://api.flexxr.at/api/v1/mobile/gdpr/export/exp-uuid/download?signature=...",
        "expires_at": "2026-06-18T12:00:00+00:00",
        "file_size": 20480
    },
    "errors": null,
    "meta": {}
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Export not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Export not found."
        }
    ],
    "meta": {}
}
 

Request      

GET api/v1/mobile/gdpr/export/{export_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

export_id   string     

The ID of the export. Example: 019febec-0321-7368-9b4e-d906c6855bac

Get Account Deletion Status

requires authentication

Returns the scheduled deletion date and cancel window, or null when no deletion is pending.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Pending):


{
    "status": "SUCCESS",
    "message": null,
    "data": {
        "scheduled_at": "2026-08-09T12:00:00+00:00",
        "can_cancel_until": "2026-08-09T12:00:00+00:00"
    },
    "errors": null,
    "meta": {}
}
 

Example response (200, None):


{
    "status": "SUCCESS",
    "message": null,
    "data": null,
    "errors": null,
    "meta": {}
}
 

Request      

GET api/v1/mobile/gdpr/delete-request

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Request Account Deletion (Art. 17 DSGVO)

requires authentication

Creates a deletion request for the user's account. The account will be scheduled for deletion after a 30-day grace period.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"No longer using the service\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "No longer using the service"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'No longer using the service',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "No longer using the service"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (400, Already Requested):


{
    "status": "BAD_REQUEST",
    "message": "Account deletion already requested.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Account deletion already requested."
        }
    ],
    "meta": {}
}
 

Request      

POST api/v1/mobile/gdpr/delete-request

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

reason   string  optional    

nullable Optional reason for account deletion. Example: No longer using the service

Cancel Account Deletion

requires authentication

Cancels a pending account deletion request during the grace period.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/delete-request');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Account deletion cancelled.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-10T12:00:00.000000Z"
    }
}
 

Example response (400, No Request):


{
    "status": "BAD_REQUEST",
    "message": "No pending deletion request.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "No pending deletion request."
        }
    ],
    "meta": {}
}
 

Request      

DELETE api/v1/mobile/gdpr/delete-request

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Withdraw GDPR Request

requires authentication

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/gdpr/requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/gdpr/requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/gdpr/requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (409, Already being handled):


{
    "status": "INVALID_OPERATION",
    "message": "Only a pending request can be withdrawn."
}
 

Request      

DELETE api/v1/mobile/gdpr/requests/{gdprRequest_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

gdprRequest_id   string     

The ID of the gdprRequest. Example: 019fe6e4-bbcd-71fd-918b-effe04df4ff9

gdprRequest   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Auth

Biometric Login

requires authentication

Exchange a device's biometric credential for a fresh session.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/login" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/login"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/login');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Signed in):


{
    "status": "SUCCESS",
    "message": "Signed in.",
    "data": {
        "token": "12|plain-access-token",
        "refresh_token": "13|plain-refresh-token",
        "biometric_token": "14|plain-biometric-token",
        "expires_in": 900
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Example response (401, Credential rejected):


{
    "status": "UNAUTHORIZED",
    "message": "This device is no longer enrolled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "This device is no longer enrolled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-07-28T10:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/biometric/login

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Confirm Password

requires authentication

Verifies the authenticated user's current password. Throttled to resist brute-force guessing.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/confirm-password" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"password\": \"S3cure-Passw0rd!\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/confirm-password"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "password": "S3cure-Passw0rd!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/confirm-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'password' => 'S3cure-Passw0rd!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/confirm-password');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "password": "S3cure-Passw0rd!"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (422, Wrong Password):


{
    "status": "UNPROCESSABLE_ENTITY",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "The provided password does not match our records."
        }
    ],
    "meta": {}
}
 

Request      

POST api/v1/mobile/auth/confirm-password

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

password   string     

Example: S3cure-Passw0rd!

Enable Biometric

requires authentication

Opt in to biometric login from an already password-authenticated session.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/enable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/enable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/enable';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/enable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Biometric login has been enabled on this device.",
    "data": {
        "biometric_token": "7|JjeW6QZpkQSEcPVxm2mduqTTTLqTnGMKVjO9MfWs2aa6a6ac"
    },
    "errors": null,
    "meta": {
        "request_id": "db4a283c-ee3b-42b8-9998-7e0ab3a0e9a4",
        "timestamp": "2026-08-05T16:18:36.051090Z"
    }
}
 

Request      

POST api/v1/mobile/auth/biometric/enable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Disable Biometric

requires authentication

Turn off biometric login and revoke the device's biometric credential.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/disable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/disable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/disable';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/biometric/disable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Biometric login has been disabled.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "e38b6ebe-f8fa-40ae-8a87-8667f88c7582",
        "timestamp": "2026-08-05T16:18:36.064570Z"
    }
}
 

Request      

POST api/v1/mobile/auth/biometric/disable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Dashboard

Get Employee Dashboard (Home Screen)

requires authentication

Returns the employee home screen data including:

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/dashboard" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/dashboard"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/dashboard';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/dashboard');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "user": {
            "id": "019fd2b7-f271-73fd-8268-848be381e136",
            "first_name": null,
            "full_name": null,
            "avatar_url": null
        },
        "next_shift": {
            "id": "019fd2b8-28d3-70d7-92eb-f1bb1577acaf",
            "status": "signed",
            "status_label": "Unterschrieben",
            "date": "2026-08-06",
            "start_time": "08:00:00",
            "end_time": "16:00:00",
            "location_name": "Nolan-Roberts",
            "location_address": "4309 O'Reilly Way Suite 012",
            "job": {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant",
                "category": {
                    "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                    "slug": "office",
                    "name": "Büro",
                    "name_localized": "Office",
                    "icon": "briefcase",
                    "color": null
                }
            },
            "organization": {
                "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                "name": "Caritas",
                "logo_url": null
            }
        },
        "profile_completion": {
            "documents_percent": 25,
            "legal_info_percent": 50,
            "total_percent": 50,
            "is_complete": false
        },
        "popular_jobs": [
            {
                "id": "019fd2b8-22cf-71c5-8edd-5e3ce9d29e8b",
                "title": "Promoter*in Vienna Marathon",
                "description": "Für ein großes Outdoor-Event suchen wir Promoter*innen und Koordinator*innen. Teamgeist und Flexibilität sind gefragt.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                    "slug": "event",
                    "name": "Events",
                    "name_localized": "Events",
                    "icon": "calendar-star",
                    "color": null
                },
                "tags": [
                    "Events",
                    "Tagschicht",
                    "Veranstaltungserfahrung",
                    "Teamfähigkeit"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Promoter*in Vienna Marathon",
                    "address": "Hauptstraße 48, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3738,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-26",
                    "end_date": "2026-08-28",
                    "shift_start_time": "09:00:00",
                    "shift_end_time": "17:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 13.5,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 20,
                    "filled": 0,
                    "available": 20
                },
                "deadlines": {
                    "application": "2026-08-23T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:30+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null
            },
            {
                "id": "019fd2b8-2599-7355-a7f1-dcb185b05faa",
                "title": "Rezeptionist*in Hotel Sacher",
                "description": "Für unser Haus suchen wir freundliche und belastbare Mitarbeiter*innen für den Front- und Back-Office-Bereich.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                    "slug": "hotel",
                    "name": "Hotellerie",
                    "name_localized": "Hospitality",
                    "icon": "hotel",
                    "color": null
                },
                "tags": [
                    "Hospitality",
                    "Tagschicht",
                    "Hotellerie-Erfahrung",
                    "Deutsch B2"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Rezeptionist*in Hotel Sacher",
                    "address": "Hauptstraße 87, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2034,
                    "lng": 16.3694,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-17",
                    "end_date": "2026-08-19",
                    "shift_start_time": "09:00:00",
                    "shift_end_time": "17:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 17.5,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 4,
                    "filled": 0,
                    "available": 4
                },
                "deadlines": {
                    "application": "2026-08-14T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:31+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null
            },
            {
                "id": "019fd2b8-1f86-7376-9a3a-c072ab5675cb",
                "title": "Service-Mitarbeiter*in Wiener Prater Festival",
                "description": "Wir suchen engagierte Service-Mitarbeiter*innen für unser Team. Du bringst Freude am Umgang mit Gästen mit und arbeitest gerne im Team.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Tagschicht",
                    "Serviceerfahrung",
                    "Deutsch B2"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Service-Mitarbeiter*in Wiener Prater Festival",
                    "address": "Hauptstraße 91, 1020 Wien",
                    "city": "Wien",
                    "postal_code": "1020",
                    "lat": 48.2117,
                    "lng": 16.3969,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-19",
                    "end_date": "2026-08-21",
                    "shift_start_time": "09:00:00",
                    "shift_end_time": "17:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 15.5,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 12,
                    "filled": 0,
                    "available": 12
                },
                "deadlines": {
                    "application": "2026-08-16T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:29+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null
            },
            {
                "id": "019fd2b8-21c8-72cc-80b1-47fd13450522",
                "title": "Barkeeper*in Sommernacht Open Air",
                "description": "Wir suchen engagierte Service-Mitarbeiter*innen für unser Team. Du bringst Freude am Umgang mit Gästen mit und arbeitest gerne im Team.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Nachtschicht",
                    "Serviceerfahrung",
                    "Deutsch B2"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Barkeeper*in Sommernacht Open Air",
                    "address": "Hauptstraße 35, 1060 Wien",
                    "city": "Wien",
                    "postal_code": "1060",
                    "lat": 48.1953,
                    "lng": 16.356,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-15",
                    "end_date": "2026-08-17",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 16,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 8,
                    "filled": 0,
                    "available": 8
                },
                "deadlines": {
                    "application": "2026-08-12T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:30+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null
            },
            {
                "id": "019fd2b8-24b0-700a-ae2a-142f5e48aead",
                "title": "Lagerhelfer*in Logistikzentrum Inzersdorf",
                "description": "Für unser Logistikzentrum suchen wir zuverlässige Helfer*innen. Körperliche Belastbarkeit und Pünktlichkeit sind Voraussetzung.",
                "status": "published",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f38b-7209-8b83-b345b172def2",
                    "slug": "warehouse",
                    "name": "Lager",
                    "name_localized": "Warehouse",
                    "icon": "industry",
                    "color": null
                },
                "tags": [
                    "Warehouse",
                    "Nachtschicht",
                    "Staplerführerschein von Vorteil",
                    "Körperliche Belastbarkeit"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Lagerhelfer*in Logistikzentrum Inzersdorf",
                    "address": "Hauptstraße 28, 1230 Wien",
                    "city": "Wien",
                    "postal_code": "1230",
                    "lat": 48.1454,
                    "lng": 16.3499,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-08-14",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 14.8,
                    "supplements": null,
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 15,
                    "filled": 0,
                    "available": 15
                },
                "deadlines": {
                    "application": "2026-08-09T00:00:00+00:00"
                },
                "created_at": "2026-08-05T16:18:31+00:00",
                "is_saved": false,
                "can_apply": false,
                "rate": null
            }
        ],
        "recent_jobs": [
            {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant",
                "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                    "slug": "office",
                    "name": "Büro",
                    "name_localized": "Office",
                    "icon": "briefcase",
                    "color": null
                },
                "tags": [
                    "Office",
                    "Nachtschicht"
                ],
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Barrows, Christiansen and Jones",
                    "address": "586 Tremaine Row",
                    "city": "East Eliseo",
                    "postal_code": "10547-3697",
                    "lat": 46.931383,
                    "lng": 11.998284,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2026-08-12",
                    "end_date": "2026-08-19",
                    "shift_start_time": "01:49:00",
                    "shift_end_time": "18:57:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 17.133333333333333
                },
                "compensation": {
                    "hourly_rate_gross": 32.08,
                    "supplements": {
                        "night": 0.7,
                        "weekend": 3.86
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 5,
                    "filled": 0,
                    "available": 5
                },
                "deadlines": {
                    "application": "2026-09-05T16:18:32+00:00"
                },
                "created_at": "2026-08-05T16:18:32+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-1405-709f-bca9-b0a418fb6a75",
                "title": "Silvesternacht Service",
                "description": "Wir suchen motivierte Silvesternacht Service für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Nachtschicht",
                    "HACCP Kenntnisse von Vorteil"
                ],
                "organization": {
                    "id": "019fd2b7-fe56-70d1-bb3a-641339ce636a",
                    "name": "Lagerhaus Steiermark",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Hotel Imperial Wien",
                    "address": "Kärntner Ring 16, 1015 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-02-09",
                    "end_date": "2027-02-10",
                    "shift_start_time": "22:00:00",
                    "shift_end_time": "06:00:00",
                    "shift_type": "night",
                    "shift_type_label": "Nachtschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 25,
                    "supplements": {
                        "night_bonus": 50,
                        "holiday_bonus": 100,
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 12,
                    "filled": 0,
                    "available": 12
                },
                "deadlines": {
                    "application": "2027-02-04T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:26+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-15b5-7019-8521-6ea79edc4ba5",
                "title": "Garderobenservice Ball",
                "description": "Für unsere bevorstehende Veranstaltung suchen wir tatkräftige Unterstützung. Als Garderobenservice Ball sind Sie das Aushängeschild unserer Veranstaltung und sorgen für einen reibungslosen Ablauf.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f398-7300-a216-c64584fea8f7",
                    "slug": "event",
                    "name": "Events",
                    "name_localized": "Events",
                    "icon": "calendar-star",
                    "color": null
                },
                "tags": [
                    "Events",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-ff32-7117-ab28-06e2d3082002",
                    "name": "Bildungszentrum Mitte",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Wiener Rathaus",
                    "address": "Friedrich-Schmidt-Platz 1, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-01-22",
                    "end_date": "2027-01-23",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 14.5,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 10,
                    "filled": 0,
                    "available": 10
                },
                "deadlines": {
                    "application": "2027-01-19T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:27+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-18d3-718f-bbe2-32c97903f559",
                "title": "Verkaufshilfe Weihnachtsgeschäft",
                "description": "Für unser Retail-Team suchen wir freundliche und kundenorientierte Verkaufshilfe Weihnachtsgeschäft. Sie beraten unsere Kunden, wickeln Kassiervorgänge ab und sorgen für eine ansprechende Warenpräsentation.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f37d-712d-92b3-aa5540f6b2a7",
                    "slug": "retail",
                    "name": "Einzelhandel",
                    "name_localized": "Retail",
                    "icon": "shopping-cart",
                    "color": null
                },
                "tags": [
                    "Retail",
                    "Tagschicht"
                ],
                "organization": {
                    "id": "019fd2b7-fca6-71cf-8683-c97f6f50a24a",
                    "name": "Reinigungsservice Alpin",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Wiener Innenstadt",
                    "address": "Kärntner Straße 15, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-01-12",
                    "end_date": "2027-02-01",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 13.5,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 10,
                    "filled": 0,
                    "available": 10
                },
                "deadlines": {
                    "application": "2027-01-08T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:28+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            },
            {
                "id": "019fd2b8-13e2-7390-a987-e12b987cdbb8",
                "title": "Weihnachtsmarkt Servicekraft",
                "description": "Wir suchen motivierte Weihnachtsmarkt Servicekraft für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                "status": "active",
                "is_featured": false,
                "category": {
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_localized": "Gastronomy",
                    "icon": "utensils",
                    "color": null
                },
                "tags": [
                    "Gastronomy",
                    "Tagschicht",
                    "HACCP Kenntnisse von Vorteil"
                ],
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "logo_url": null,
                    "average_rating": null
                },
                "location": {
                    "name": "Christkindlmarkt Rathausplatz",
                    "address": "Rathausplatz 1, 1010 Wien",
                    "city": "Wien",
                    "postal_code": "1010",
                    "lat": 48.2082,
                    "lng": 16.3739,
                    "distance_km": null
                },
                "schedule": {
                    "start_date": "2027-01-02",
                    "end_date": "2027-02-01",
                    "shift_start_time": "08:00:00",
                    "shift_end_time": "16:00:00",
                    "shift_type": "day",
                    "shift_type_label": "Tagschicht",
                    "duration_hours": 8
                },
                "compensation": {
                    "hourly_rate_gross": 14,
                    "supplements": {
                        "weekend_bonus": 8
                    },
                    "estimated_total_gross": null,
                    "currency": "EUR"
                },
                "vacancies": {
                    "total": 30,
                    "filled": 0,
                    "available": 30
                },
                "deadlines": {
                    "application": "2026-12-29T16:18:26+00:00"
                },
                "created_at": "2026-08-05T16:18:26+00:00",
                "is_saved": false,
                "can_apply": true,
                "rate": null
            }
        ],
        "recommended_jobs": [
            {
                "job": {
                    "id": "019fd2b8-1a90-7125-af02-72f37fef65ea",
                    "title": "Backoffice Assistenz",
                    "description": "Wir suchen engagierte Backoffice Assistenz zur Verstärkung unseres Teams. Sie erwartet ein abwechslungsreiches Aufgabengebiet in einem dynamischen Umfeld.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f39f-73e6-870b-fe7584e4f9bc",
                        "slug": "office",
                        "name": "Büro",
                        "name_localized": "Office",
                        "icon": "briefcase",
                        "color": null
                    },
                    "tags": [
                        "Office",
                        "Tagschicht"
                    ],
                    "organization": {
                        "id": "019fd2b7-f768-7325-b254-280a67b1a995",
                        "name": "Retail Services GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Steuerberatung Wien",
                        "address": "Margaretenstraße 90, 1050 Wien",
                        "city": "Wien",
                        "postal_code": "1050",
                        "lat": 48.1944,
                        "lng": 16.3585,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-15",
                        "end_date": "2026-09-04",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 16,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 1,
                        "filled": 0,
                        "available": 1
                    },
                    "deadlines": {
                        "application": "2026-08-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:28+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 61.5,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 10,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1265-737c-ae21-e5265dc709d8",
                    "title": "Servicekraft Restaurant Mitte",
                    "description": "Wir suchen motivierte Servicekraft Restaurant Mitte für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-ff32-7117-ab28-06e2d3082002",
                        "name": "Bildungszentrum Mitte",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Brasserie Mitte",
                        "address": "Schwarzenbergplatz 3, 1010 Wien",
                        "city": "Wien",
                        "postal_code": "1010",
                        "lat": 48.2082,
                        "lng": 16.3739,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-10",
                        "end_date": "2026-09-09",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 14.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 8,
                        "filled": 0,
                        "available": 8
                    },
                    "deadlines": {
                        "application": "2026-08-07T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1287-7146-a673-6798f3d03acc",
                    "title": "Barkeeper Spätschicht Club",
                    "description": "Wir suchen motivierte Barkeeper Spätschicht Club für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Nachtschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-f5ae-7390-a778-c08c8d56cdc0",
                        "name": "Event Solutions Austria",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Club Passage",
                        "address": "Babenbergerstraße 9, 1010 Wien",
                        "city": "Wien",
                        "postal_code": "1070",
                        "lat": 48.2003,
                        "lng": 16.3465,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-08",
                        "end_date": "2026-11-03",
                        "shift_start_time": "22:00:00",
                        "shift_end_time": "06:00:00",
                        "shift_type": "night",
                        "shift_type_label": "Nachtschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 16,
                        "supplements": {
                            "night_bonus": 15,
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 4,
                        "filled": 0,
                        "available": 4
                    },
                    "deadlines": {
                        "application": "2026-08-06T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-1298-737e-bbfd-295992feb4db",
                    "title": "Küchenhilfe Großküche",
                    "description": "Wir suchen motivierte Küchenhilfe Großküche für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fad7-719c-887a-a0f510bbd31d",
                        "name": "IT Solutions GmbH",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "SV Gastronomie Wien",
                        "address": "Gablenzgasse 11, 1150 Wien",
                        "city": "Wien",
                        "postal_code": "1150",
                        "lat": 48.196,
                        "lng": 16.3268,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-12",
                        "end_date": "2026-12-03",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 13.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 10,
                        "filled": 0,
                        "available": 10
                    },
                    "deadlines": {
                        "application": "2026-08-07T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-12b2-72ff-8be7-dadbf8e167b6",
                    "title": "Kellner*in Haubenrestaurant",
                    "description": "Wir suchen motivierte Kellner*in Haubenrestaurant für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fca6-71cf-8683-c97f6f50a24a",
                        "name": "Reinigungsservice Alpin",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Restaurant Steirereck",
                        "address": "Am Heumarkt 2a, 1030 Wien",
                        "city": "Wien",
                        "postal_code": "1030",
                        "lat": 48.2014,
                        "lng": 16.387,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-15",
                        "end_date": "2026-10-04",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 15.5,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 3,
                        "filled": 0,
                        "available": 3
                    },
                    "deadlines": {
                        "application": "2026-08-10T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            },
            {
                "job": {
                    "id": "019fd2b8-12c7-72ab-bd18-53060b989f57",
                    "title": "Buffetkraft Messe",
                    "description": "Wir suchen motivierte Buffetkraft Messe für unser engagiertes Team. Sie unterstützen uns bei der professionellen Gästebetreuung und sorgen für ein unvergessliches Erlebnis. Vorkenntnisse in der Gastronomie sind von Vorteil, aber nicht zwingend erforderlich.",
                    "status": "active",
                    "is_featured": false,
                    "category": {
                        "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                        "slug": "gastro",
                        "name": "Gastronomie",
                        "name_localized": "Gastronomy",
                        "icon": "utensils",
                        "color": null
                    },
                    "tags": [
                        "Gastronomy",
                        "Tagschicht",
                        "HACCP Kenntnisse von Vorteil"
                    ],
                    "organization": {
                        "id": "019fd2b7-fe56-70d1-bb3a-641339ce636a",
                        "name": "Lagerhaus Steiermark",
                        "logo_url": null,
                        "average_rating": null
                    },
                    "location": {
                        "name": "Reed Messe Wien",
                        "address": "Messeplatz 1, 1021 Wien",
                        "city": "Wien",
                        "postal_code": "1020",
                        "lat": 48.2176,
                        "lng": 16.4138,
                        "distance_km": null
                    },
                    "schedule": {
                        "start_date": "2026-08-19",
                        "end_date": "2026-08-21",
                        "shift_start_time": "08:00:00",
                        "shift_end_time": "16:00:00",
                        "shift_type": "day",
                        "shift_type_label": "Tagschicht",
                        "duration_hours": 8
                    },
                    "compensation": {
                        "hourly_rate_gross": 13.8,
                        "supplements": {
                            "weekend_bonus": 8
                        },
                        "estimated_total_gross": null,
                        "currency": "EUR"
                    },
                    "vacancies": {
                        "total": 15,
                        "filled": 0,
                        "available": 15
                    },
                    "deadlines": {
                        "application": "2026-08-15T16:18:26+00:00"
                    },
                    "created_at": "2026-08-05T16:18:26+00:00",
                    "is_saved": false,
                    "can_apply": true,
                    "rate": null
                },
                "recommendation": {
                    "score": 60,
                    "reasons": [
                        "Passt zu Ihren Qualifikationen"
                    ],
                    "breakdown": {
                        "location": 50,
                        "qualifications": 100,
                        "category_history": 0,
                        "company_rating": 50,
                        "reliability": 100
                    }
                }
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/dashboard

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Qualifications

List My Qualifications

requires authentication

Each with its verification state and expiry. An expired qualification is kept on the record but no longer counts towards a job that requires it.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/qualifications" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/qualifications';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/qualifications');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "qualifications": [],
        "counts": {
            "total": 0,
            "verified": 0,
            "pending": 0,
            "expiring_soon": 0,
            "expired": 0
        }
    }
}
 

Request      

GET api/v1/mobile/qualifications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Add a Qualification

requires authentication

A certificate or licence the worker holds — a Staplerschein, a food hygiene certificate, a first-aid course. Jobs can require these, and an unverified or expired qualification does not satisfy the requirement.

Attach the supporting scan by uploading it via POST /documents first and passing the resulting id as document_id; a qualification with no evidence cannot be verified.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Staplerschein\",
    \"description\": \"Gabelstapler bis 5t\",
    \"issuing_organization\": \"WIFI Wien\",
    \"credential_id\": \"ST-2024-8891\",
    \"document_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"issued_at\": \"2024-03-01\",
    \"expires_at\": \"2029-03-01\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Staplerschein",
    "description": "Gabelstapler bis 5t",
    "issuing_organization": "WIFI Wien",
    "credential_id": "ST-2024-8891",
    "document_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "issued_at": "2024-03-01",
    "expires_at": "2029-03-01"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/qualifications';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Staplerschein',
            'description' => 'Gabelstapler bis 5t',
            'issuing_organization' => 'WIFI Wien',
            'credential_id' => 'ST-2024-8891',
            'document_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'issued_at' => '2024-03-01',
            'expires_at' => '2029-03-01',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/qualifications');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Staplerschein",
    "description": "Gabelstapler bis 5t",
    "issuing_organization": "WIFI Wien",
    "credential_id": "ST-2024-8891",
    "document_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "issued_at": "2024-03-01",
    "expires_at": "2029-03-01"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The qualification has been added successfully.",
    "data": {
        "qualification": {
            "id": "019fd41b-85f2-713f-88ff-c02f3cca8cb7",
            "name": "Staplerschein",
            "description": "Gabelstapler bis 5t",
            "issuing_organization": "WIFI Wien",
            "credential_id": "ST-2024-8891",
            "issued_at": "2024-03-01",
            "expires_at": "2029-03-01",
            "is_verified": false,
            "created_at": "2026-08-05 22:46:41"
        }
    }
}
 

Request      

POST api/v1/mobile/qualifications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

What the qualification is. Example: Staplerschein

description   string  optional    

Free text, if the name alone is unclear. Example: Gabelstapler bis 5t

issuing_organization   string  optional    

Who issued it. Example: WIFI Wien

credential_id   string  optional    

Certificate or licence number. Example: ST-2024-8891

document_id   string  optional    

An uploaded document evidencing it. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

issued_at   date  optional    

Date of issue; cannot be in the future. Example: 2024-03-01

expires_at   date  optional    

Expiry date; must be in the future. Example: 2029-03-01

Update a Qualification

requires authentication

Editing a verified qualification returns it to review — the details a reviewer checked are no longer the details on file.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Beispieltext\",
    \"description\": \"Beispieltext\",
    \"issuing_organization\": \"Beispieltext\",
    \"credential_id\": \"Beispieltext\",
    \"document_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"issued_at\": \"2026-09-30\",
    \"expires_at\": \"2026-09-30\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Beispieltext",
    "description": "Beispieltext",
    "issuing_organization": "Beispieltext",
    "credential_id": "Beispieltext",
    "document_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "issued_at": "2026-09-30",
    "expires_at": "2026-09-30"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Beispieltext',
            'description' => 'Beispieltext',
            'issuing_organization' => 'Beispieltext',
            'credential_id' => 'Beispieltext',
            'document_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'issued_at' => '2026-09-30',
            'expires_at' => '2026-09-30',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Beispieltext",
    "description": "Beispieltext",
    "issuing_organization": "Beispieltext",
    "credential_id": "Beispieltext",
    "document_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "issued_at": "2026-09-30",
    "expires_at": "2026-09-30"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/mobile/qualifications/{qualificationId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

qualificationId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

name   string  optional    

Example: Beispieltext

description   string  optional    

Example: Beispieltext

issuing_organization   string  optional    

Example: Beispieltext

credential_id   string  optional    

Example: Beispieltext

document_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

issued_at   date  optional    

Example: 2026-09-30

expires_at   date  optional    

Example: 2026-09-30

Delete a Qualification

requires authentication

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/mobile/qualifications/{qualificationId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

qualificationId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Bank Accounts

List My Bank Accounts

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "bank_accounts": [],
        "has_primary": false
    }
}
 

Request      

GET api/v1/mobile/bank-accounts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Add a Bank Account

requires authentication

Where wages are paid. A bank account is one of the sections a profile needs before it can be submitted for approval.

The IBAN is checksum-validated, not merely length-checked, and the BIC is derived from the Austrian bank code when it is not supplied.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"account_holder\": \"Jonas Brunner\",
    \"iban\": \"AT611904300234573201\",
    \"bic\": \"GIBAATWWXXX\",
    \"bank_name\": \"Erste Bank\",
    \"is_primary\": true,
    \"verification_document_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "account_holder": "Jonas Brunner",
    "iban": "AT611904300234573201",
    "bic": "GIBAATWWXXX",
    "bank_name": "Erste Bank",
    "is_primary": true,
    "verification_document_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'account_holder' => 'Jonas Brunner',
            'iban' => 'AT611904300234573201',
            'bic' => 'GIBAATWWXXX',
            'bank_name' => 'Erste Bank',
            'is_primary' => true,
            'verification_document_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "account_holder": "Jonas Brunner",
    "iban": "AT611904300234573201",
    "bic": "GIBAATWWXXX",
    "bank_name": "Erste Bank",
    "is_primary": true,
    "verification_document_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (422, Invalid IBAN):


{
    "status": "VALIDATION_ERROR",
    "message": "The IBAN is not valid."
}
 

Request      

POST api/v1/mobile/bank-accounts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

account_holder   string     

Name on the account. Payroll may reject a mismatch with the worker's legal name. Example: Jonas Brunner

iban   string     

IBAN, validated by checksum. Example: AT611904300234573201

bic   string  optional    

BIC/SWIFT. Derived from the IBAN for Austrian banks when omitted. Example: GIBAATWWXXX

bank_name   string  optional    

Name of the bank. Example: Erste Bank

is_primary   boolean  optional    

Make this the account wages are sent to. Example: true

verification_document_id   string  optional    

An uploaded bank statement evidencing the account. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Set the Primary Bank Account

requires authentication

Chooses which account wages are paid into. Exactly one account is primary at a time; promoting one demotes the other.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/primary" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/primary"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/primary';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/primary');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/mobile/bank-accounts/{accountId}/primary

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

accountId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Delete a Bank Account

requires authentication

The primary account cannot be removed while it is the only one on file — a worker with no account cannot be paid.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/bank-accounts/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/mobile/bank-accounts/{accountId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

accountId   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Security

Get Two-Factor State

requires authentication

Returns the current 2FA configuration for the authenticated user.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/security/two-factor" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/security/two-factor"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/security/two-factor';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/security/two-factor');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, 2FA Disabled):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "enabled": false,
        "method": null,
        "confirmed_at": null,
        "recovery_codes_remaining": 0
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-10T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "enabled": true,
        "method": "totp",
        "confirmed_at": "2026-08-05T16:18:32+00:00",
        "recovery_codes_remaining": 2
    },
    "errors": null,
    "meta": {
        "request_id": "4bb5ce3f-11eb-4ddd-b32e-09a6493765be",
        "timestamp": "2026-08-05T16:18:36.072911Z"
    }
}
 

Request      

GET api/v1/mobile/security/two-factor

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Login History

requires authentication

Returns recent login attempts for the authenticated user.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/security/login-history?limit=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/security/login-history"
);

const params = {
    "limit": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/security/login-history';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'limit' => '20',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/security/login-history')
      .replace(queryParameters: {
        'limit': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "0e90e127-69d7-4ef7-9364-6d17d1842b2c",
        "timestamp": "2026-08-05T16:18:36.080326Z"
    }
}
 

Request      

GET api/v1/mobile/security/login-history

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

limit   integer  optional    

nullable Maximum entries to return (min 1, max 50, default 20). Example: 20

Regenerate Recovery Codes

requires authentication

Generates new recovery codes, invalidating all previous ones. User must have 2FA enabled.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/security/recovery-codes/regenerate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/security/recovery-codes/regenerate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/security/recovery-codes/regenerate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/security/recovery-codes/regenerate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "New recovery codes have been generated.",
    "data": {
        "recovery_codes": [
            "HB2T0-PKB9V",
            "RMEKC-3DIJH",
            "C3VY7-WKMA1",
            "DWBQ6-EOGR1",
            "USUS6-PPZO7",
            "6M3U3-5CG0V",
            "HCSH0-CYUBB",
            "MSNEO-XIOLK"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "1ed862c5-9edb-4fb3-9167-c4f0a29ed7f5",
        "timestamp": "2026-08-05T16:18:36.089383Z"
    }
}
 

Example response (400, 2FA Not Enabled):


{
    "status": "BAD_REQUEST",
    "message": "Two-factor authentication is not enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-10T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/security/recovery-codes/regenerate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Settings

Get Notification Settings

requires authentication

Get user notification settings.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "global": {
            "email_enabled": true,
            "push_enabled": true,
            "sms_enabled": true,
            "marketing_enabled": false
        },
        "categories": {
            "security": [
                {
                    "type": "registration_welcome",
                    "label": "Willkommen bei Flexxr",
                    "description": "Willkommensnachricht nach der Registrierung mit Link zur E-Mail-Bestätigung",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "new_device_login",
                    "label": "Anmeldung von neuem Gerät",
                    "description": "Benachrichtigung wenn sich jemand von einem neuen Gerät anmeldet",
                    "can_disable": false,
                    "supports_push": true,
                    "supports_sms": true,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "password_changed",
                    "label": "Passwort geändert",
                    "description": "Benachrichtigung bei Passwortänderung",
                    "can_disable": false,
                    "supports_push": true,
                    "supports_sms": true,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "two_factor_enabled",
                    "label": "Zwei-Faktor-Authentifizierung aktiviert",
                    "description": "Benachrichtigung wenn 2FA aktiviert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "two_factor_disabled",
                    "label": "Zwei-Faktor-Authentifizierung deaktiviert",
                    "description": "Benachrichtigung wenn 2FA deaktiviert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "account_suspended",
                    "label": "Konto gesperrt",
                    "description": "Benachrichtigung wenn Ihr Konto gesperrt wird",
                    "can_disable": false,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "account_reactivated",
                    "label": "Konto reaktiviert",
                    "description": "Benachrichtigung wenn Ihr Konto reaktiviert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                }
            ],
            "profile": [
                {
                    "type": "profile_approved",
                    "label": "Profil genehmigt",
                    "description": "Benachrichtigung wenn Ihr Profil genehmigt wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "profile_rejected",
                    "label": "Profil abgelehnt",
                    "description": "Benachrichtigung wenn Ihr Profil abgelehnt wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "document_verified",
                    "label": "Dokument verifiziert",
                    "description": "Benachrichtigung wenn ein Dokument verifiziert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "document_rejected",
                    "label": "Dokument abgelehnt",
                    "description": "Benachrichtigung wenn ein Dokument abgelehnt wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "document_expiring",
                    "label": "Dokument läuft ab",
                    "description": "Erinnerung wenn ein Dokument bald abläuft",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "correction_required",
                    "label": "Korrektur erforderlich",
                    "description": "Benachrichtigung wenn Korrekturen erforderlich sind",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "review_taking_longer",
                    "label": "Prüfung dauert länger",
                    "description": "Benachrichtigung wenn die Profilprüfung länger dauert als erwartet",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "profile_incomplete_reminder",
                    "label": "Profil unvollständig – Erinnerung",
                    "description": "Erinnerung wenn Ihr Profil seit mehreren Tagen unvollständig ist",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "work_permit_expiring",
                    "label": "Arbeitserlaubnis läuft ab",
                    "description": "Erinnerung wenn Ihre Arbeitserlaubnis bald abläuft",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "work_permit_expired_suspension",
                    "label": "Konto gesperrt – Arbeitserlaubnis abgelaufen",
                    "description": "Benachrichtigung wenn Ihr Konto aufgrund einer abgelaufenen Arbeitserlaubnis gesperrt wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "profile_submitted",
                    "label": "Unterlagen werden geprüft",
                    "description": "Benachrichtigung wenn Ihre Unterlagen zur Prüfung eingereicht wurden",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                }
            ],
            "jobs": [
                {
                    "type": "new_job_match",
                    "label": "Neuer passender Job",
                    "description": "Benachrichtigung über neue passende Jobs",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "saved_job_deadline",
                    "label": "Bewerbungsfrist endet bald",
                    "description": "Erinnerung, bevor die Bewerbungsfrist eines gemerkten Jobs endet",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "application_received",
                    "label": "Bewerbung eingegangen",
                    "description": "Bestätigung über eingegangene Bewerbungen",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "application_accepted",
                    "label": "Bewerbung angenommen",
                    "description": "Benachrichtigung wenn eine Bewerbung angenommen wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "application_rejected",
                    "label": "Bewerbung abgelehnt",
                    "description": "Benachrichtigung wenn eine Bewerbung abgelehnt wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "application_waitlisted",
                    "label": "Auf Warteliste gesetzt",
                    "description": "Benachrichtigung wenn Sie auf die Warteliste gesetzt werden",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_assigned",
                    "label": "Schicht zugewiesen",
                    "description": "Benachrichtigung wenn Ihnen eine Schicht zugewiesen wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_reminder",
                    "label": "Schicht-Erinnerung",
                    "description": "Erinnerung vor Schichtbeginn",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": true,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_cancelled",
                    "label": "Schicht storniert",
                    "description": "Benachrichtigung wenn eine Schicht storniert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": true,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_modified",
                    "label": "Schicht geändert",
                    "description": "Benachrichtigung wenn eine Schicht geändert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_location_changed",
                    "label": "Einsatzort geändert",
                    "description": "Benachrichtigung wenn der Einsatzort einer Schicht geändert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_extended",
                    "label": "Schicht verlängert",
                    "description": "Benachrichtigung wenn eine Schicht verlängert wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "shift_time_disputed",
                    "label": "Arbeitszeit bestritten",
                    "description": "Benachrichtigung wenn die erfasste Arbeitszeit bestritten wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "no_show_detected",
                    "label": "No-Show erkannt",
                    "description": "Benachrichtigung wenn ein Mitarbeiter nicht erscheint",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "no_show_recorded",
                    "label": "No-Show vermerkt",
                    "description": "Mitteilung über vermerkte Abwesenheit",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "emergency_shift_available",
                    "label": "Notfall-Schicht verfügbar",
                    "description": "Benachrichtigung über kurzfristig verfügbare Schichten",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "emergency_shift_assigned",
                    "label": "Notfall-Schicht zugewiesen",
                    "description": "Bestätigung einer Notfall-Schichtzuweisung",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "replacement_found",
                    "label": "Ersatz gefunden",
                    "description": "Benachrichtigung wenn Ersatz gefunden wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                }
            ],
            "finance": [
                {
                    "type": "payslip_available",
                    "label": "Lohnzettel verfügbar",
                    "description": "Benachrichtigung wenn ein neuer Lohnzettel verfügbar ist",
                    "can_disable": true,
                    "supports_push": false,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": false,
                    "sms_enabled": false
                },
                {
                    "type": "payment_sent",
                    "label": "Zahlung gesendet",
                    "description": "Benachrichtigung wenn eine Zahlung gesendet wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "payment_processing",
                    "label": "Auszahlung in Bearbeitung",
                    "description": "Benachrichtigung wenn eine Auszahlung in Bearbeitung ist",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "invoice_issued",
                    "label": "Rechnung ausgestellt",
                    "description": "Benachrichtigung wenn eine Rechnung ausgestellt wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "payment_due",
                    "label": "Zahlung fällig",
                    "description": "Erinnerung wenn eine Zahlung fällig wird",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "payment_overdue",
                    "label": "Zahlung überfällig",
                    "description": "Benachrichtigung wenn eine Zahlung überfällig ist",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "wallet_topped_up",
                    "label": "Wallet aufgeladen",
                    "description": "Benachrichtigung wenn Ihr Wallet erfolgreich aufgeladen wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "withdrawal_requested",
                    "label": "Auszahlung angefordert",
                    "description": "Benachrichtigung wenn eine Auszahlung zur Prüfung eingereicht wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "withdrawal_completed",
                    "label": "Auszahlung abgeschlossen",
                    "description": "Benachrichtigung wenn eine Auszahlung erfolgreich abgeschlossen wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "withdrawal_failed",
                    "label": "Auszahlung fehlgeschlagen",
                    "description": "Benachrichtigung wenn eine Auszahlung fehlgeschlagen ist",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "withdrawal_rejected",
                    "label": "Auszahlung abgelehnt",
                    "description": "Benachrichtigung wenn eine Auszahlung abgelehnt wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "withdrawal_cancelled",
                    "label": "Auszahlung storniert",
                    "description": "Benachrichtigung wenn eine Auszahlung storniert wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "platform_fee_charged",
                    "label": "Plattformgebühr berechnet",
                    "description": "Benachrichtigung wenn eine Plattformgebühr berechnet wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "payslip_ready",
                    "label": "Lohnzettel bereit",
                    "description": "Benachrichtigung wenn der Lohnzettel bereit ist",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "invoice_created",
                    "label": "Rechnung erstellt",
                    "description": "Benachrichtigung über neue Rechnungen",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                }
            ],
            "communication": [
                {
                    "type": "new_message",
                    "label": "Neue Nachricht",
                    "description": "Benachrichtigung über neue Nachrichten",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "new_rating",
                    "label": "Neue Bewertung",
                    "description": "Benachrichtigung über neue Bewertungen",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "support_ticket_update",
                    "label": "Support-Ticket aktualisiert",
                    "description": "Aktualisierungen zu Ihren Support-Tickets",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "support_ticket_reply",
                    "label": "Support hat geantwortet",
                    "description": "Benachrichtigung wenn der Support auf Ihr Ticket antwortet",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "support_ticket_resolved",
                    "label": "Support-Ticket gelöst",
                    "description": "Benachrichtigung wenn Ihr Support-Ticket gelöst wurde",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                }
            ],
            "system": [
                {
                    "type": "compliance_violation",
                    "label": "Compliance-Verstoß",
                    "description": "Benachrichtigung über Arbeitszeitverstöße",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                },
                {
                    "type": "system_notification",
                    "label": "System-Benachrichtigung",
                    "description": "Allgemeine System-Benachrichtigungen",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": true,
                    "push_enabled": true,
                    "sms_enabled": false
                }
            ],
            "marketing": [
                {
                    "type": "marketing_email",
                    "label": "Marketing E-Mails",
                    "description": "Werbliche E-Mails und Angebote",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": false,
                    "push_enabled": false,
                    "sms_enabled": false
                },
                {
                    "type": "marketing_push",
                    "label": "Marketing Push-Nachrichten",
                    "description": "Werbliche Push-Nachrichten",
                    "can_disable": true,
                    "supports_push": true,
                    "supports_sms": false,
                    "email_enabled": false,
                    "push_enabled": false,
                    "sms_enabled": false
                },
                {
                    "type": "newsletter",
                    "label": "Newsletter",
                    "description": "Regelmäßiger Newsletter mit Neuigkeiten",
                    "can_disable": true,
                    "supports_push": false,
                    "supports_sms": false,
                    "email_enabled": false,
                    "push_enabled": false,
                    "sms_enabled": false
                }
            ]
        }
    }
}
 

Request      

GET api/v1/mobile/settings/notifications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Notification Setting

requires authentication

Update user notification settings.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"notification_type\": \"architecto\",
    \"email_enabled\": true,
    \"push_enabled\": true,
    \"sms_enabled\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "notification_type": "architecto",
    "email_enabled": true,
    "push_enabled": true,
    "sms_enabled": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'notification_type' => 'architecto',
            'email_enabled' => true,
            'push_enabled' => true,
            'sms_enabled' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "notification_type": "architecto",
    "email_enabled": true,
    "push_enabled": true,
    "sms_enabled": false
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/mobile/settings/notifications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

notification_type   string     

Example: architecto

email_enabled   boolean  optional    

Example: true

push_enabled   boolean  optional    

Example: true

sms_enabled   boolean  optional    

Example: false

Update Global Notification Settings

requires authentication

Update global notification settings.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications/global" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email_enabled\": true,
    \"push_enabled\": true,
    \"sms_enabled\": false,
    \"marketing_enabled\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications/global"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email_enabled": true,
    "push_enabled": true,
    "sms_enabled": false,
    "marketing_enabled": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications/global';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email_enabled' => true,
            'push_enabled' => true,
            'sms_enabled' => false,
            'marketing_enabled' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/settings/notifications/global');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email_enabled": true,
    "push_enabled": true,
    "sms_enabled": false,
    "marketing_enabled": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "email_enabled": false,
        "push_enabled": false,
        "sms_enabled": true,
        "marketing_enabled": true
    }
}
 

Request      

PUT api/v1/mobile/settings/notifications/global

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email_enabled   boolean  optional    

Example: true

push_enabled   boolean  optional    

Example: true

sms_enabled   boolean  optional    

Example: false

marketing_enabled   boolean  optional    

Example: true

Get App Settings

requires authentication

Returns user's app settings (language, theme, biometric auth).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/settings/app" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/app"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/settings/app';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/settings/app');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "settings": {
            "language": "de",
            "theme": "system",
            "biometric_enabled": false,
            "push_notifications_enabled": true,
            "location_tracking_enabled": false,
            "email_notifications_enabled": true
        },
        "available_languages": [
            {
                "code": "de",
                "label": "Deutsch"
            },
            {
                "code": "en",
                "label": "English"
            }
        ],
        "available_themes": [
            {
                "value": "system",
                "label": "System"
            },
            {
                "value": "light",
                "label": "Hell"
            },
            {
                "value": "dark",
                "label": "Dunkel"
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/settings/app

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update App Settings

requires authentication

Updates user's app settings (language, theme, biometric auth).

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/app" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"language\": \"de\",
    \"theme\": \"dark\",
    \"biometric_enabled\": true,
    \"push_notifications_enabled\": true,
    \"location_tracking_enabled\": false,
    \"email_notifications_enabled\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/settings/app"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "language": "de",
    "theme": "dark",
    "biometric_enabled": true,
    "push_notifications_enabled": true,
    "location_tracking_enabled": false,
    "email_notifications_enabled": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/settings/app';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'language' => 'de',
            'theme' => 'dark',
            'biometric_enabled' => true,
            'push_notifications_enabled' => true,
            'location_tracking_enabled' => false,
            'email_notifications_enabled' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/settings/app');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "language": "de",
    "theme": "dark",
    "biometric_enabled": true,
    "push_notifications_enabled": true,
    "location_tracking_enabled": false,
    "email_notifications_enabled": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "settings": {
            "language": "de",
            "theme": "dark",
            "biometric_enabled": true,
            "push_notifications_enabled": true,
            "location_tracking_enabled": false,
            "email_notifications_enabled": true
        },
        "message": "settings.updated"
    }
}
 

Request      

PUT api/v1/mobile/settings/app

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

language   string  optional    

Language code: de, en. Allowed: de, en. Example: de

theme   string  optional    

Theme: system, light, dark. Allowed: system, light, dark. Example: dark

biometric_enabled   boolean  optional    

Enable biometric authentication. Example: true

push_notifications_enabled   boolean  optional    

Enable push notifications. Example: true

location_tracking_enabled   boolean  optional    

Enable location tracking. Example: false

email_notifications_enabled   boolean  optional    

Enable email notifications. Example: true

Job Alerts

List Job Alerts

requires authentication

Returns all job alerts for the authenticated employee.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/job-alerts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/job-alerts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": [
        {
            "id": "019fd2b8-2e42-73c3-89fd-86a26426e4ba",
            "name": "Lager Wien",
            "category": null,
            "location_city": null,
            "location_state": null,
            "min_hourly_rate": null,
            "max_hourly_rate": null,
            "shift_type": null,
            "max_distance_km": null,
            "keywords": null,
            "is_active": true,
            "notification_frequency": "instant",
            "push_enabled": true,
            "email_enabled": false,
            "last_matched_at": null,
            "total_matches": 0,
            "created_at": "2026-08-05T16:18:33+00:00"
        }
    ]
}
 

Request      

GET api/v1/mobile/job-alerts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Job Alert

requires authentication

Creates a new job alert for the authenticated employee. Maximum 10 active alerts per user.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Gastro Jobs Wien\",
    \"category_id\": \"019abcd-1234\",
    \"location_city\": \"Wien\",
    \"location_state\": \"wien\",
    \"min_hourly_rate\": 14,
    \"max_hourly_rate\": 25,
    \"shift_type\": \"day\",
    \"max_distance_km\": 25,
    \"location_lat\": 48.2082,
    \"location_lng\": 16.3738,
    \"keywords\": [
        \"kellner\",
        \"service\"
    ],
    \"notification_frequency\": \"instant\",
    \"push_enabled\": true,
    \"email_enabled\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Gastro Jobs Wien",
    "category_id": "019abcd-1234",
    "location_city": "Wien",
    "location_state": "wien",
    "min_hourly_rate": 14,
    "max_hourly_rate": 25,
    "shift_type": "day",
    "max_distance_km": 25,
    "location_lat": 48.2082,
    "location_lng": 16.3738,
    "keywords": [
        "kellner",
        "service"
    ],
    "notification_frequency": "instant",
    "push_enabled": true,
    "email_enabled": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/job-alerts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Gastro Jobs Wien',
            'category_id' => '019abcd-1234',
            'location_city' => 'Wien',
            'location_state' => 'wien',
            'min_hourly_rate' => 14.0,
            'max_hourly_rate' => 25.0,
            'shift_type' => 'day',
            'max_distance_km' => 25,
            'location_lat' => 48.2082,
            'location_lng' => 16.3738,
            'keywords' => ['kellner', 'service'],
            'notification_frequency' => 'instant',
            'push_enabled' => true,
            'email_enabled' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/job-alerts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Gastro Jobs Wien",
    "category_id": "019abcd-1234",
    "location_city": "Wien",
    "location_state": "wien",
    "min_hourly_rate": 14,
    "max_hourly_rate": 25,
    "shift_type": "day",
    "max_distance_km": 25,
    "location_lat": 48.2082,
    "location_lng": 16.3738,
    "keywords": [
        "kellner",
        "service"
    ],
    "notification_frequency": "instant",
    "push_enabled": true,
    "email_enabled": false
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Created):


{
    "status": "SUCCESS",
    "data": {}
}
 

Example response (422, Too many alerts):


{
    "status": "ERROR",
    "message": "Maximum 10 active alerts allowed"
}
 

Request      

POST api/v1/mobile/job-alerts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

The alert name. Example: Gastro Jobs Wien

category_id   uuid  optional    

Filter by job category. Example: 019abcd-1234

location_city   string  optional    

Filter by city. Example: Wien

location_state   string  optional    

Filter by Austrian state. Example: wien

min_hourly_rate   number  optional    

Minimum hourly rate. Example: 14

max_hourly_rate   number  optional    

Maximum hourly rate. Example: 25

shift_type   string  optional    

Shift type filter. Allowed: day, night, weekend, holiday. Example: day

max_distance_km   integer  optional    

Search radius in km. Example: 25

location_lat   number  optional    

Latitude for radius search. Example: 48.2082

location_lng   number  optional    

Longitude for radius search. Example: 16.3738

keywords   string[]  optional    

Keywords to match in title/description.

notification_frequency   string  optional    

Notification frequency. Allowed: instant, daily, weekly. Example: instant

push_enabled   boolean  optional    

Enable push notifications. Example: true

email_enabled   boolean  optional    

Enable email notifications. Example: false

Update Job Alert

requires authentication

Updates an existing job alert for the authenticated employee.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Gastro Jobs Wien\",
    \"category_id\": \"019abcd-1234\",
    \"location_city\": \"Wien\",
    \"location_state\": \"wien\",
    \"min_hourly_rate\": 14,
    \"max_hourly_rate\": 25,
    \"shift_type\": \"day\",
    \"max_distance_km\": 25,
    \"location_lat\": 48.2082,
    \"location_lng\": 16.3738,
    \"keywords\": [
        \"kellner\",
        \"service\"
    ],
    \"is_active\": true,
    \"notification_frequency\": \"instant\",
    \"push_enabled\": true,
    \"email_enabled\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Gastro Jobs Wien",
    "category_id": "019abcd-1234",
    "location_city": "Wien",
    "location_state": "wien",
    "min_hourly_rate": 14,
    "max_hourly_rate": 25,
    "shift_type": "day",
    "max_distance_km": 25,
    "location_lat": 48.2082,
    "location_lng": 16.3738,
    "keywords": [
        "kellner",
        "service"
    ],
    "is_active": true,
    "notification_frequency": "instant",
    "push_enabled": true,
    "email_enabled": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Gastro Jobs Wien',
            'category_id' => '019abcd-1234',
            'location_city' => 'Wien',
            'location_state' => 'wien',
            'min_hourly_rate' => 14.0,
            'max_hourly_rate' => 25.0,
            'shift_type' => 'day',
            'max_distance_km' => 25,
            'location_lat' => 48.2082,
            'location_lng' => 16.3738,
            'keywords' => ['kellner', 'service'],
            'is_active' => true,
            'notification_frequency' => 'instant',
            'push_enabled' => true,
            'email_enabled' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Gastro Jobs Wien",
    "category_id": "019abcd-1234",
    "location_city": "Wien",
    "location_state": "wien",
    "min_hourly_rate": 14,
    "max_hourly_rate": 25,
    "shift_type": "day",
    "max_distance_km": 25,
    "location_lat": 48.2082,
    "location_lng": 16.3738,
    "keywords": [
        "kellner",
        "service"
    ],
    "is_active": true,
    "notification_frequency": "instant",
    "push_enabled": true,
    "email_enabled": false
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Updated):


{
    "status": "SUCCESS",
    "data": {}
}
 

Example response (404, Not found):


{
    "status": "ERROR",
    "message": "Job alert not found"
}
 

Request      

PUT api/v1/mobile/job-alerts/{alertId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

alertId   string  optional    

uuid required The job alert ID. Example: 019abcd-5678

Body Parameters

name   string  optional    

The alert name. Example: Gastro Jobs Wien

category_id   uuid  optional    

Filter by job category. Example: 019abcd-1234

location_city   string  optional    

Filter by city. Example: Wien

location_state   string  optional    

Filter by Austrian state. Example: wien

min_hourly_rate   number  optional    

Minimum hourly rate. Example: 14

max_hourly_rate   number  optional    

Maximum hourly rate. Example: 25

shift_type   string  optional    

Shift type filter. Allowed: day, night, weekend, holiday. Example: day

max_distance_km   integer  optional    

Search radius in km. Example: 25

location_lat   number  optional    

Latitude for radius search. Example: 48.2082

location_lng   number  optional    

Longitude for radius search. Example: 16.3738

keywords   string[]  optional    

Keywords to match in title/description.

is_active   boolean  optional    

Whether the alert is active. Example: true

notification_frequency   string  optional    

Notification frequency. Allowed: instant, daily, weekly. Example: instant

push_enabled   boolean  optional    

Enable push notifications. Example: true

email_enabled   boolean  optional    

Enable email notifications. Example: false

Delete Job Alert

requires authentication

Deletes a job alert for the authenticated employee.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/job-alerts/019abcd-5678');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Job alert deleted"
}
 

Example response (404, Not found):


{
    "status": "ERROR",
    "message": "Job alert not found"
}
 

Request      

DELETE api/v1/mobile/job-alerts/{alertId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

alertId   string  optional    

uuid required The job alert ID. Example: 019abcd-5678

Badges

List Badges

requires authentication

List all available badges with their status for the authenticated employee.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/badges" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/badges"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/badges';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/badges');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "summary": {
        "total_badges": 0,
        "earned_badges": 0,
        "total_points": 0,
        "completion_percent": 0
    },
    "badges": []
}
 

Request      

GET api/v1/mobile/badges

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Earned Badges

requires authentication

List the authenticated employee's earned badges.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/badges/earned" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/badges/earned"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/badges/earned';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/badges/earned');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "total_badges": 0,
    "total_points": 0,
    "badges": []
}
 

Request      

GET api/v1/mobile/badges/earned

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Earnings

Get Earnings Summary

requires authentication

Returns earnings breakdown from completed shifts. Supports period filtering (week/month/year/all).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/earnings?period=month&start_date=2026-01-01&end_date=2026-06-30" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/earnings"
);

const params = {
    "period": "month",
    "start_date": "2026-01-01",
    "end_date": "2026-06-30",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/earnings';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'period' => 'month',
            'start_date' => '2026-01-01',
            'end_date' => '2026-06-30',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/earnings')
      .replace(queryParameters: {
        'period': 'month',
        'start_date': '2026-01-01',
        'end_date': '2026-06-30',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "period": {
            "type": "month",
            "start": "2026-01-01",
            "end": "2026-06-30",
            "label": "01.01.2026 - 30.06.2026"
        },
        "summary": {
            "total_gross": 0,
            "total_net_estimated": 0,
            "total_hours": 0,
            "shifts_count": 0,
            "avg_hourly_rate": 0
        },
        "breakdown": {
            "paid": {
                "amount": 0,
                "hours": 0,
                "shifts_count": 0
            },
            "pending": {
                "amount": 0,
                "hours": 0,
                "shifts_count": 0
            }
        },
        "by_category": [],
        "by_organization": [],
        "chart_data": [
            {
                "date": "2026-01-01",
                "label": "01",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-02",
                "label": "02",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-03",
                "label": "03",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-04",
                "label": "04",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-05",
                "label": "05",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-06",
                "label": "06",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-07",
                "label": "07",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-08",
                "label": "08",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-09",
                "label": "09",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-10",
                "label": "10",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-11",
                "label": "11",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-12",
                "label": "12",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-13",
                "label": "13",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-14",
                "label": "14",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-15",
                "label": "15",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-16",
                "label": "16",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-17",
                "label": "17",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-18",
                "label": "18",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-19",
                "label": "19",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-20",
                "label": "20",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-21",
                "label": "21",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-22",
                "label": "22",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-23",
                "label": "23",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-24",
                "label": "24",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-25",
                "label": "25",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-26",
                "label": "26",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-27",
                "label": "27",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-28",
                "label": "28",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-29",
                "label": "29",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-30",
                "label": "30",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-01-31",
                "label": "31",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-01",
                "label": "01",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-02",
                "label": "02",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-03",
                "label": "03",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-04",
                "label": "04",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-05",
                "label": "05",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-06",
                "label": "06",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-07",
                "label": "07",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-08",
                "label": "08",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-09",
                "label": "09",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-10",
                "label": "10",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-11",
                "label": "11",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-12",
                "label": "12",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-13",
                "label": "13",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-14",
                "label": "14",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-15",
                "label": "15",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-16",
                "label": "16",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-17",
                "label": "17",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-18",
                "label": "18",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-19",
                "label": "19",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-20",
                "label": "20",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-21",
                "label": "21",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-22",
                "label": "22",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-23",
                "label": "23",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-24",
                "label": "24",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-25",
                "label": "25",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-26",
                "label": "26",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-27",
                "label": "27",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-02-28",
                "label": "28",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-01",
                "label": "01",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-02",
                "label": "02",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-03",
                "label": "03",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-04",
                "label": "04",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-05",
                "label": "05",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-06",
                "label": "06",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-07",
                "label": "07",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-08",
                "label": "08",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-09",
                "label": "09",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-10",
                "label": "10",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-11",
                "label": "11",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-12",
                "label": "12",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-13",
                "label": "13",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-14",
                "label": "14",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-15",
                "label": "15",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-16",
                "label": "16",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-17",
                "label": "17",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-18",
                "label": "18",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-19",
                "label": "19",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-20",
                "label": "20",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-21",
                "label": "21",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-22",
                "label": "22",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-23",
                "label": "23",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-24",
                "label": "24",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-25",
                "label": "25",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-26",
                "label": "26",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-27",
                "label": "27",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-28",
                "label": "28",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-29",
                "label": "29",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-30",
                "label": "30",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-03-31",
                "label": "31",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-01",
                "label": "01",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-02",
                "label": "02",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-03",
                "label": "03",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-04",
                "label": "04",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-05",
                "label": "05",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-06",
                "label": "06",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-07",
                "label": "07",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-08",
                "label": "08",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-09",
                "label": "09",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-10",
                "label": "10",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-11",
                "label": "11",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-12",
                "label": "12",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-13",
                "label": "13",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-14",
                "label": "14",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-15",
                "label": "15",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-16",
                "label": "16",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-17",
                "label": "17",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-18",
                "label": "18",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-19",
                "label": "19",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-20",
                "label": "20",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-21",
                "label": "21",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-22",
                "label": "22",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-23",
                "label": "23",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-24",
                "label": "24",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-25",
                "label": "25",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-26",
                "label": "26",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-27",
                "label": "27",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-28",
                "label": "28",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-29",
                "label": "29",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-04-30",
                "label": "30",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-01",
                "label": "01",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-02",
                "label": "02",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-03",
                "label": "03",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-04",
                "label": "04",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-05",
                "label": "05",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-06",
                "label": "06",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-07",
                "label": "07",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-08",
                "label": "08",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-09",
                "label": "09",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-10",
                "label": "10",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-11",
                "label": "11",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-12",
                "label": "12",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-13",
                "label": "13",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-14",
                "label": "14",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-15",
                "label": "15",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-16",
                "label": "16",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-17",
                "label": "17",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-18",
                "label": "18",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-19",
                "label": "19",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-20",
                "label": "20",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-21",
                "label": "21",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-22",
                "label": "22",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-23",
                "label": "23",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-24",
                "label": "24",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-25",
                "label": "25",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-26",
                "label": "26",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-27",
                "label": "27",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-28",
                "label": "28",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-29",
                "label": "29",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-30",
                "label": "30",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-05-31",
                "label": "31",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-01",
                "label": "01",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-02",
                "label": "02",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-03",
                "label": "03",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-04",
                "label": "04",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-05",
                "label": "05",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-06",
                "label": "06",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-07",
                "label": "07",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-08",
                "label": "08",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-09",
                "label": "09",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-10",
                "label": "10",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-11",
                "label": "11",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-12",
                "label": "12",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-13",
                "label": "13",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-14",
                "label": "14",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-15",
                "label": "15",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-16",
                "label": "16",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-17",
                "label": "17",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-18",
                "label": "18",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-19",
                "label": "19",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-20",
                "label": "20",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-21",
                "label": "21",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-22",
                "label": "22",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-23",
                "label": "23",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-24",
                "label": "24",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-25",
                "label": "25",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-26",
                "label": "26",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-27",
                "label": "27",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-28",
                "label": "28",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-29",
                "label": "29",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            },
            {
                "date": "2026-06-30",
                "label": "30",
                "gross": 0,
                "hours": 0,
                "shifts": 0
            }
        ],
        "navigation": {
            "prev": "2025-12",
            "next": "2026-02"
        }
    }
}
 

Request      

GET api/v1/mobile/earnings

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

period   string  optional    

Time period: week, month, year, all. Example: month

start_date   string  optional    

date Custom start date. Example: 2026-01-01

end_date   string  optional    

date Custom end date. Example: 2026-06-30

Reliability

Get Reliability Score

requires authentication

Get the employee's reliability score and history.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/reliability" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/reliability"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/reliability';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/reliability');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "reliability": {
            "score": 100,
            "tier": "excellent",
            "tier_label": "Excellent",
            "window_start": "2026-02-05",
            "window_end": "2026-08-05"
        },
        "punctuality": {
            "percentage": null
        },
        "activity": {
            "hours_worked": 0,
            "shifts_completed": 1
        },
        "breakdown": {
            "shift_cancellation": 0,
            "no_show": 0,
            "shift_completion": 0,
            "positive_feedback": 0
        },
        "recent_negative": [],
        "improvement_tips": [
            "Build your work history by completing more shifts."
        ]
    }
}
 

Request      

GET api/v1/mobile/reliability

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Contracts

List Contracts

requires authentication

List all contracts for the authenticated employee.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/contracts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/contracts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/contracts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/contracts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "contracts": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/mobile/contracts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Contract

requires authentication

Get contract details and download URL.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "1b3b99d9-6927-4218-9edf-6c81f2bb5ebf",
        "timestamp": "2026-08-21T05:14:34.095638Z"
    }
}
 

Request      

GET api/v1/mobile/contracts/{contractId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

contractId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Sign Contract

requires authentication

The worker signs a contract — the framework agreement before applying to anything, or an Überlassungsvertrag.

A selection produces one Überlassungsvertrag per shift, not one for the batch: a company can set contract terms on a single shift, so each shift is agreed to on its own. A worker selected for three days has three to sign, and signing one readies that shift alone — the others stay pending_signature with their own deadlines.

Two things are required together: an explicit consent flag, and the signature they drew. The drawing is stamped onto the PDF and bound into its hash, so a contract cannot later be altered without the signature ceasing to match.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/sign" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"signature_consent\": true,
    \"signature_data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/sign"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "signature_consent": true,
    "signature_data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/sign';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'signature_consent' => true,
            'signature_data' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/contracts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/sign');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "signature_consent": true,
    "signature_data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/mobile/contracts/{contractId}/sign

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

contractId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

signature_consent   boolean     

The worker's agreement to be bound by the contract. Must be true — anything else is refused. Example: true

signature_data   string     

The signature the worker drew, as base64 PNG or JPEG. The data:image/png;base64, prefix is optional — raw base64 from a canvas is accepted and stored with the prefix added. The format is read from the decoded bytes, so a prefix that disagrees with the image is refused. Max 500 KB. Stamped onto the PDF and bound into its tamper-evidence hash. Example: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==

Support

List Tickets

requires authentication

List all support tickets for the authenticated user.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/support/tickets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/support/tickets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "tickets": [
            {
                "id": "019fd2b8-2e3b-7096-97f9-96d4a481c803",
                "ticket_number": "TKT-969367",
                "category": "complaint",
                "category_label": "Beschwerde",
                "status": "open",
                "status_label": "Offen",
                "subject": "Totam autem repellendus quasi iste debitis fugiat.",
                "priority": 2,
                "created_at": "2026-08-05 16:18",
                "resolved_at": null,
                "has_unread": false
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/mobile/support/tickets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Ticket

requires authentication

Create a new support ticket (worker requester).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"category\": \"general\",
    \"subject\": \"Frage zur Abrechnung\",
    \"description\": \"Beispieltext\",
    \"related_entity_type\": \"shift\",
    \"related_entity_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "category": "general",
    "subject": "Frage zur Abrechnung",
    "description": "Beispieltext",
    "related_entity_type": "shift",
    "related_entity_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/support/tickets';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'category' => 'general',
            'subject' => 'Frage zur Abrechnung',
            'description' => 'Beispieltext',
            'related_entity_type' => 'shift',
            'related_entity_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/support/tickets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "category": "general",
    "subject": "Frage zur Abrechnung",
    "description": "Beispieltext",
    "related_entity_type": "shift",
    "related_entity_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/mobile/support/tickets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

category   string     

Allowed: general, shift, payment, contract, account, technical, complaint, profile_change, document, other. Example: general

subject   string     

Example: Frage zur Abrechnung

description   string     

Example: Beispieltext

related_entity_type   string  optional    

Allowed: shift, contract, job. Example: shift

related_entity_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Get Ticket

requires authentication

Get a support ticket with messages.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "605f390a-b139-4dc7-838f-1ae2277ef187",
        "timestamp": "2026-08-21T05:14:34.145889Z"
    }
}
 

Request      

GET api/v1/mobile/support/tickets/{ticketId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Add Ticket Message

requires authentication

Add a message to an existing ticket.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"message\": \"Bitte um Rueckmeldung zur naechsten Schicht.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'message' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/mobile/support/tickets/{ticketId}/messages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

message   string     

Example: Bitte um Rueckmeldung zur naechsten Schicht.

Messages

List Conversations

requires authentication

List conversations for the authenticated mobile user.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "conversations": [
            {
                "id": "64c3f8d8-55d2-4457-b394-c3277a378bac",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "logo_url": null
                },
                "job": null,
                "last_message": null,
                "unread_count": 0,
                "last_message_at": "2026-08-05T16:18:32+00:00"
            }
        ]
    }
}
 

Request      

GET api/v1/mobile/messages/conversations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Messages

requires authentication

Get messages for a conversation (mobile user perspective).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "messages": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 50,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/mobile/messages/conversations/{conversation_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

conversation_id   string     

The ID of the conversation. Example: 019fd51d-53c7-73d0-880d-8edee1800329

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reply To Conversation

requires authentication

Reply to an existing conversation (mobile user perspective).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"content\": \"Beispieltext\",
    \"attachments\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "content": "Beispieltext",
    "attachments": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'content' => 'Beispieltext',
            'attachments' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "content": "Beispieltext",
    "attachments": null
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "message": {
            "id": "019fd2b8-3b63-72cd-84f1-aeb06cd822ba",
            "conversation_id": "64c3f8d8-55d2-4457-b394-c3277a378bac",
            "content": "Beispieltext",
            "is_from_user": true,
            "created_at": "2026-08-05T16:18:37+00:00"
        }
    }
}
 

Request      

POST api/v1/mobile/messages/conversations/{conversation_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

conversation_id   string     

The ID of the conversation. Example: 019fd51d-53c7-73d0-880d-8edee1800329

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

content   string  optional    

Required unless attachments are present. Example: Beispieltext

attachments   string[]  optional    

IDs from the attachment upload endpoint.

Upload a Message Attachment

requires authentication

Upload a file attachment ahead of sending it with a message. The returned attachment id is bound to a message when that message is sent.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "file=@/tmp/phpau36t4abcslnf4xzzmv" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'file',
                'contents' => fopen('/tmp/phpau36t4abcslnf4xzzmv', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/phpau36t4abcslnf4xzzmv'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (201, Uploaded):


{
    "status": "SUCCESS",
    "data": {
        "attachment": {
            "id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
            "name": "beleg.pdf",
            "mime_type": "application/pdf",
            "file_size": 48213
        }
    }
}
 

Request      

POST api/v1/mobile/messages/conversations/{conversation}/attachments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

file   file     

The photo or document. Example: /tmp/phpau36t4abcslnf4xzzmv

Send Message

requires authentication

Send a message to an organization from the mobile user.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/messages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"content\": \"Beispieltext\",
    \"job_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"shift_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"attachments\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "content": "Beispieltext",
    "job_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "shift_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "attachments": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'content' => 'Beispieltext',
            'job_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'shift_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'attachments' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/messages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "content": "Beispieltext",
    "job_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "shift_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "attachments": null
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "message": {
            "id": "019fd2b8-3b7b-706f-9077-c559f02b838a",
            "conversation_id": "019fd2b8-3b78-703e-bc22-0286223114aa",
            "content": "Beispieltext",
            "created_at": "2026-08-05T16:18:37+00:00"
        }
    }
}
 

Request      

POST api/v1/mobile/messages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

organization_id   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

content   string  optional    

Required unless attachments are present. Example: Beispieltext

job_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

shift_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

attachments   string[]  optional    

IDs from the attachment upload endpoint.

Admin API

Admin dashboard endpoints for platform management. Requires admin-level Bearer token authentication.

Authentication

Login Admin

Authenticates an administrator against the admin guard and issues a Sanctum token. Inactive admin accounts are rejected before credential verification to prevent timing-based enumeration of deactivated accounts.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"admin@flexxr.at\",
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\",
    \"password\": \"AdminSecure123\",
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\",
    \"remember_me\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "admin@flexxr.at",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "AdminSecure123",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH",
    "remember_me": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'admin@flexxr.at',
            'country_code' => '+43',
            'phone_number' => '6641234567',
            'password' => 'AdminSecure123',
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
            'remember_me' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/login');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "admin@flexxr.at",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "AdminSecure123",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH",
    "remember_me": true
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Login successful.",
    "data": {
        "token": "14|Qd0Pxqa8ulXyaGJEB8Ylpxe6EUYq02DMJzPtUTV5e9f2582f",
        "admin": {
            "id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
            "name": "Super Admin",
            "email": "office@flexxr.at",
            "role": "super_admin",
            "is_active": true,
            "last_login_at": "2026-08-05T16:18:40.000000Z",
            "created_at": "2026-08-05T16:18:17.000000Z"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "46530e7b-851c-4aab-b7fe-66d6253aeabb",
        "timestamp": "2026-08-05T16:18:40.986452Z"
    }
}
 

Example response (401, 2FA Required):


{
    "status": "TWO_FACTOR_REQUIRED",
    "message": "Two-factor authentication is required.",
    "data": {
        "token": "2|challenge-token...",
        "two_factor_method": "totp"
    },
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Invalid Credentials):


{
    "status": "INVALID_CREDENTIALS",
    "message": "The provided credentials are incorrect.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The provided credentials are incorrect."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The email field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (423, Account Temporarily Locked):


{
    "status": "ACCOUNT_TEMPORARILY_LOCKED",
    "message": "Account temporarily locked. Try again in 15 minutes.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Account temporarily locked. Try again in 15 minutes."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Admin email address. Example: admin@flexxr.at

country_code   string  optional    

Country code with + prefix. Required together with phone_number for phone login. This field is required when phone_number is present. Must match the regex /^+\d{1,4}$/. Must not be greater than 5 characters. Example: +43

phone_number   string  optional    

Phone number without country code. Required together with country_code for phone login. This field is required when country_code is present. Must match the regex /^\d{6,15}$/. Must not be greater than 15 characters. Example: 6641234567

password   string     

Plain-text password (min 8 characters). Example: AdminSecure123

code   string  optional    

Optional 2FA one-time code (TOTP or email OTP) for single-step 2FA login. Example: 123456

recovery_code   string  optional    

Optional 2FA recovery code in place of code. Example: ABCD-1234-EFGH

remember_me   boolean  optional    

Example: true

Dashboard

Get Admin Dashboard Stats

requires authentication

Aggregate counts for the admin dashboard's KPI tiles.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/dashboard/stats" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/dashboard/stats"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/dashboard/stats';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/dashboard/stats');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "pending_employees": 1,
        "pending_companies": 1,
        "active_jobs": 109,
        "ongoing_assignments": 1,
        "overdue_invoices": 0,
        "open_tickets": 2,
        "fraud_alerts": 0
    },
    "errors": null,
    "meta": {
        "request_id": "3a5fb11c-8caa-4c3e-8d97-0efd8c8c83bc",
        "timestamp": "2026-08-05T16:18:41.679890Z"
    }
}
 

Request      

GET api/v1/admin/dashboard/stats

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Users

List Users

requires authentication

Returns a paginated list of all users, optionally filtered by a search term (matched against name and email) and/or a status value.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/users?search=test&status=active&per_page=15&sort=first_name&order=asc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users"
);

const params = {
    "search": "test",
    "status": "active",
    "per_page": "15",
    "sort": "first_name",
    "order": "asc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'search' => 'test',
            'status' => 'active',
            'per_page' => '15',
            'sort' => 'first_name',
            'order' => 'asc',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users')
      .replace(queryParameters: {
        'search': 'test',
        'status': 'active',
        'per_page': '15',
        'sort': 'first_name',
        'order': 'asc',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "b0862681-9b77-4cd1-bf0b-b07077be4ce1",
        "timestamp": "2026-08-05T16:18:41.691650Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 0
    }
}
 

Request      

GET api/v1/admin/users

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

search   string  optional    

Search keyword. Example: test

status   string  optional    

Filter by status. Example: active

per_page   integer  optional    

Number of items per page. Example: 15

sort   string  optional    

Column to sort by. Example: first_name

order   string  optional    

"asc" or "desc". Example: asc

Returns the full profile of a single user, identified by route-model binding.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "User retrieved successfully.",
    "data": {
        "id": "019fd2b7-f271-73fd-8268-848be381e136",
        "first_name": "Anna",
        "last_name": "Neuling",
        "email": "pending@demo.flexxr.at",
        "country_code": "+43",
        "phone_number": "6769876543",
        "status": "active",
        "avatar_url": null,
        "bio": null,
        "date_of_birth": null,
        "country": null,
        "city": null,
        "notify_push": true,
        "notify_email": true,
        "notify_sms": true,
        "notify_marketing": false,
        "email_verified_at": "2026-08-05T16:18:32+00:00",
        "phone_verified_at": "2026-08-05T16:18:32+00:00",
        "created_at": "2026-08-05T16:18:18+00:00",
        "updated_at": "2026-08-05T16:18:33+00:00",
        "employee_profile": null
    },
    "errors": null,
    "meta": {
        "request_id": "f29df2ed-2159-4b94-a318-cb28112ce82a",
        "timestamp": "2026-08-05T16:18:41.701057Z"
    }
}
 

Request      

GET api/v1/admin/users/{user_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Updates an employee's profile fields (identity, contact, address) on behalf of an admin.

requires authentication

Status transitions, password rotations, anonymisation and email-change confirmations all have dedicated endpoints with their own audit events and are intentionally not handled here.

Address fields (address_line_1, address_line_2, postal_code) are stored on the employee_profiles table, while identity and basic location (city, country) remain on the users table.

GDPR Art. 30 record-of-processing is satisfied by writing a user.updated_by_admin audit event listing only the names of the fields that changed — never the cleartext values themselves. The new values remain authoritative on the User/EmployeeProfile rows, the previous values can be reconstructed from immutable backups if a forensic review is needed.

Example request:
curl --request PATCH \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"first_name\": \"Beispieltext\",
    \"last_name\": \"Beispieltext\",
    \"bio\": \"Beispieltext\",
    \"date_of_birth\": \"2026-09-30\",
    \"country_code\": \"AT\",
    \"phone_number\": \"6641234567\",
    \"address_line_1\": \"Beispieltext\",
    \"address_line_2\": \"Beispieltext\",
    \"postal_code\": \"AT\",
    \"city\": \"Beispieltext\",
    \"country\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "first_name": "Beispieltext",
    "last_name": "Beispieltext",
    "bio": "Beispieltext",
    "date_of_birth": "2026-09-30",
    "country_code": "AT",
    "phone_number": "6641234567",
    "address_line_1": "Beispieltext",
    "address_line_2": "Beispieltext",
    "postal_code": "AT",
    "city": "Beispieltext",
    "country": "Beispieltext"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'first_name' => 'Beispieltext',
            'last_name' => 'Beispieltext',
            'bio' => 'Beispieltext',
            'date_of_birth' => '2026-09-30',
            'country_code' => 'AT',
            'phone_number' => '6641234567',
            'address_line_1' => 'Beispieltext',
            'address_line_2' => 'Beispieltext',
            'postal_code' => 'AT',
            'city' => 'Beispieltext',
            'country' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "first_name": "Beispieltext",
    "last_name": "Beispieltext",
    "bio": "Beispieltext",
    "date_of_birth": "2026-09-30",
    "country_code": "AT",
    "phone_number": "6641234567",
    "address_line_1": "Beispieltext",
    "address_line_2": "Beispieltext",
    "postal_code": "AT",
    "city": "Beispieltext",
    "country": "Beispieltext"
};

  final response = await http.patch(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PATCH api/v1/admin/users/{user_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Body Parameters

first_name   string  optional    

Example: Beispieltext

last_name   string  optional    

Example: Beispieltext

bio   string  optional    

Example: Beispieltext

date_of_birth   date  optional    

Example: 2026-09-30

country_code   string     

Example: AT

phone_number   string     

Example: 6641234567

address_line_1   string  optional    

Example: Beispieltext

address_line_2   string  optional    

Example: Beispieltext

postal_code   string  optional    

Example: AT

city   string  optional    

Example: Beispieltext

country   string  optional    

Example: Beispieltext

Updates the status of an existing user account.

requires authentication

Delegates the status transition (including token revocation for

deactivated/banned users) to UserService. * @group Admin API

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"active\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "active"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/status';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'active',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "active"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "User status updated successfully.",
    "data": {
        "id": "019fd2b7-f271-73fd-8268-848be381e136",
        "first_name": "Anna",
        "last_name": "Neuling",
        "email": "pending@demo.flexxr.at",
        "country_code": "+43",
        "phone_number": "6769876543",
        "status": "active",
        "avatar_url": null,
        "bio": null,
        "date_of_birth": null,
        "country": null,
        "city": null,
        "notify_push": true,
        "notify_email": true,
        "notify_sms": true,
        "notify_marketing": false,
        "email_verified_at": "2026-08-05T16:18:32+00:00",
        "phone_verified_at": "2026-08-05T16:18:32+00:00",
        "created_at": "2026-08-05T16:18:18+00:00",
        "updated_at": "2026-08-05T16:18:33+00:00",
        "employee_profile": null
    },
    "errors": null,
    "meta": {
        "request_id": "b855ec49-4087-41ca-8107-b867ec67bdb0",
        "timestamp": "2026-08-05T16:18:41.723038Z"
    }
}
 

Request      

PUT api/v1/admin/users/{user_id}/status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Body Parameters

status   string     

Allowed: active, banned, deactivated. Example: active

Admin-triggered user password reset email.

requires authentication

Sends the user a 6-digit code using the same code-based flow as self-service forgot-password. The mobile app handles the code entry.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/send-reset" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/send-reset"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/send-reset';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/send-reset');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password reset email sent.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "95f42427-a958-4322-af06-e36c986887c8",
        "timestamp": "2026-08-05T16:18:41.738923Z"
    }
}
 

Request      

POST api/v1/admin/users/{user_id}/send-reset

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Admin Users

Creates a new admin account.

requires authentication

The super-admin route middleware enforces the role restriction. The newly created admin is active by default; they must log in and optionally enable 2FA themselves. No welcome email is sent — the

super admin communicates credentials out-of-band. * @group Admin API

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/admins" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Beispieltext\",
    \"email\": \"name@example.at\",
    \"password\": \"S3cure-Passw0rd!\",
    \"role\": \"super_admin\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Beispieltext",
    "email": "name@example.at",
    "password": "S3cure-Passw0rd!",
    "role": "super_admin"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Beispieltext',
            'email' => 'name@example.at',
            'password' => 'S3cure-Passw0rd!',
            'role' => 'super_admin',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Beispieltext",
    "email": "name@example.at",
    "password": "S3cure-Passw0rd!",
    "role": "super_admin"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "CREATED",
    "message": "Admin created successfully.",
    "data": {
        "id": "019fd2b8-520f-7027-b522-08e9a33b49b3",
        "name": "Beispieltext",
        "email": "name@example.at",
        "role": "super_admin",
        "is_active": true,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:42.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "8cdc19e4-93ff-4e3f-b229-fee0e489d02b",
        "timestamp": "2026-08-05T16:18:42.843825Z"
    }
}
 

Request      

POST api/v1/admin/admins

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Example: Beispieltext

email   string     

Example: name@example.at

password   string     

Example: S3cure-Passw0rd!

role   string     

Allowed: super_admin, payroll_admin, payroll_agent, compliance_officer, compliance_agent, support_supervisor, support_agent, ops_manager, ops_agent, finance_admin, finance_agent. Example: super_admin

Returns the profile of a single admin, identified by route-model binding. * @group Admin API

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Admin retrieved successfully.",
    "data": {
        "id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "name": "Super Admin",
        "email": "office@flexxr.at",
        "role": "super_admin",
        "is_active": true,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:17.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "b038e712-95bd-458e-89ed-02e96b78f138",
        "timestamp": "2026-08-05T16:18:42.853899Z"
    }
}
 

Request      

GET api/v1/admin/admins/{admin_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Changes the role of an existing admin account.

requires authentication

Guards against self-demotion: a super admin cannot downgrade their own role, preventing accidental lock-out when they are the only super admin.

On every successful role change, the matching ACL preset is also swapped so the legacy admin.role column and the acl_actor_presets pivot stay in sync. The role update and the preset swap run inside one transaction.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/role" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"role\": \"super_admin\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/role"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "role": "super_admin"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/role';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'role' => 'super_admin',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/role');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "role": "super_admin"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Admin role updated successfully.",
    "data": {
        "id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "name": "Super Admin",
        "email": "office@flexxr.at",
        "role": "super_admin",
        "is_active": true,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:17.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "cc178b32-00a9-4fb3-8c59-eeeb3d3fb9c5",
        "timestamp": "2026-08-05T16:18:42.871916Z"
    }
}
 

Request      

PUT api/v1/admin/admins/{admin_id}/role

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Body Parameters

role   string     

Allowed: super_admin, payroll_admin, payroll_agent, compliance_officer, compliance_agent, support_supervisor, support_agent, ops_manager, ops_agent, finance_admin, finance_agent. Example: super_admin

Deactivates an admin account and revokes all their Sanctum tokens.

requires authentication

Guards against self-deactivation to prevent a super admin from

accidentally locking themselves out of the dashboard. * @group Admin API

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/deactivate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/deactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/deactivate';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/deactivate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Admin deactivated successfully.",
    "data": {
        "id": "019fd2b8-29c6-70eb-b55e-61878b3ad363",
        "name": "Timothy Goyette I",
        "email": "klein.jeffrey@example.com",
        "role": "support_agent",
        "is_active": false,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:32.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "043c8f6f-b451-4f1f-8aa3-a1b9ad17ee99",
        "timestamp": "2026-08-05T16:18:42.890951Z"
    }
}
 

Request      

PUT api/v1/admin/admins/{admin_id}/deactivate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Reactivates a previously deactivated admin account.

requires authentication

Tokens are not restored — the admin must log in fresh to obtain a new token. * @group Admin API

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reactivate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reactivate';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reactivate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Admin reactivated successfully.",
    "data": {
        "id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "name": "Super Admin",
        "email": "office@flexxr.at",
        "role": "super_admin",
        "is_active": true,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:17.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "3be88090-98cd-49d7-91f3-780bc1ad10a4",
        "timestamp": "2026-08-05T16:18:42.902995Z"
    }
}
 

Request      

PUT api/v1/admin/admins/{admin_id}/reactivate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Resets (disables) the 2FA setting for a target admin account.

requires authentication

Mirrors the mechanism used by DisableTwoFactorAction: deletes the TwoFactorSetting morph record for the target authenticatable. Also revokes all Sanctum tokens so the current session cannot coast on the 2fa.verified state.

Guards against self-reset — super admins must use the self-service

DELETE /admin/auth/two-factor/disable endpoint instead. * @group Admin API

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-two-factor" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-two-factor"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-two-factor';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-two-factor');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication reset successfully.",
    "data": {
        "id": "019fd2b8-29c6-70eb-b55e-61878b3ad363",
        "name": "Timothy Goyette I",
        "email": "klein.jeffrey@example.com",
        "role": "support_agent",
        "is_active": true,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:32.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "5b6c0487-98b5-47e0-9beb-af10a7656f0a",
        "timestamp": "2026-08-05T16:18:42.919563Z"
    }
}
 

Request      

POST api/v1/admin/admins/{admin_id}/reset-two-factor

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Resets (disables) the 2FA setting for a target admin account.

requires authentication

Mirrors the mechanism used by DisableTwoFactorAction: deletes the TwoFactorSetting morph record for the target authenticatable. Also revokes all Sanctum tokens so the current session cannot coast on the 2fa.verified state.

Guards against self-reset — super admins must use the self-service

DELETE /admin/auth/two-factor/disable endpoint instead. * @group Admin API

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-2fa" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-2fa"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-2fa';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/reset-2fa');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication reset successfully.",
    "data": {
        "id": "019fd2b8-29c6-70eb-b55e-61878b3ad363",
        "name": "Timothy Goyette I",
        "email": "klein.jeffrey@example.com",
        "role": "support_agent",
        "is_active": true,
        "last_login_at": null,
        "created_at": "2026-08-05T16:18:32.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "5b6c0487-98b5-47e0-9beb-af10a7656f0a",
        "timestamp": "2026-08-05T16:18:42.919563Z"
    }
}
 

Request      

POST api/v1/admin/admins/{admin_id}/reset-2fa

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Get Scoped Admin Presets

requires authentication

Returns the preset keys currently active for a single admin in the given organisation context (union of global + org-scoped assignments). Omitting organization_id returns the same result as the global endpoint.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped?organization_id=018f1234-abcd-7000-0000-000000000001" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped"
);

const params = {
    "organization_id": "018f1234-abcd-7000-0000-000000000001",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'organization_id' => '018f1234-abcd-7000-0000-000000000001',
        ],
        'json' => [
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped')
      .replace(queryParameters: {
        'organization_id': '018f1234-abcd-7000-0000-000000000001',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "organization_id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "presets": [
            "super-admin"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "f5f405f3-eeec-4e79-abc8-7b8920e1b156",
        "timestamp": "2026-08-05T16:18:42.948008Z"
    }
}
 

Request      

GET api/v1/admin/admins/{admin_id}/presets/scoped

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Query Parameters

organization_id   string  optional    

optional UUID of the organisation to scope to. Example: 018f1234-abcd-7000-0000-000000000001

Body Parameters

organization_id   string  optional    

Must be a valid UUID. Must match an existing stored value. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Update Scoped Admin Presets

requires authentication

Bulk-replaces the org-scoped preset assignments for an admin. Only rows matching the supplied organization_id context are diffed; global assignments are never touched. Presets absent from the request body are revoked (soft); new ones are assigned. The diff and writes run inside a single DB transaction.

Required permission: admin.admins.manage (super-admin only by default).

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"018f1234-abcd-7000-0000-000000000001\",
    \"presets\": [
        \"admin\",
        \"admin-finance\"
    ]
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "018f1234-abcd-7000-0000-000000000001",
    "presets": [
        "admin",
        "admin-finance"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'organization_id' => '018f1234-abcd-7000-0000-000000000001',
            'presets' => ['admin', 'admin-finance'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets/scoped');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "018f1234-abcd-7000-0000-000000000001",
    "presets": [
        "admin",
        "admin-finance"
    ]
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Scoped admin presets updated.",
    "data": {
        "admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "organization_id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "presets": [
            "super-admin"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "dc0fa735-54c1-46d5-9457-cc38dd13b0e1",
        "timestamp": "2026-08-05T16:18:42.969201Z"
    }
}
 

Example response (422, Unknown preset):


{
    "status": "VALIDATION_ERROR",
    "message": "Unknown preset 'foo'."
}
 

Example response (422, Out of scope):


{
    "status": "VALIDATION_ERROR",
    "message": "Unknown preset 'co-admin'."
}
 

Request      

PUT api/v1/admin/admins/{admin_id}/presets/scoped

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Body Parameters

organization_id   string     

UUID of the organisation to scope to. Example: 018f1234-abcd-7000-0000-000000000001

presets   string[]     

List of admin preset keys scoped to this org. Pass [] to revoke all scoped presets.

Get Admin Presets

requires authentication

Returns the preset keys currently assigned to a single admin. Used by the "Manage roles" dialog to pre-check the right boxes.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "presets": [
            "super-admin"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "e65c3abe-ffe9-4fee-8269-dea01f566b5b",
        "timestamp": "2026-08-05T16:18:42.981025Z"
    }
}
 

Request      

GET api/v1/admin/admins/{admin_id}/presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Update Admin Presets

requires authentication

Bulk-replaces the presets assigned to an admin. Presets currently held but absent from the request body are revoked; presets in the request body but not yet held are added. The diff and the assign / revoke writes execute inside a single transaction so a partial failure cannot leave the actor with a half-updated preset set.

Required permission: admin.admins.manage (super-admin only by default).

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"presets\": [
        \"admin\",
        \"admin-finance\"
    ]
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "presets": [
        "admin",
        "admin-finance"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'presets' => ['admin', 'admin-finance'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "presets": [
        "admin",
        "admin-finance"
    ]
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Admin presets updated.",
    "data": {
        "admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "presets": [
            "super-admin"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "8204b89c-9e5a-4051-b179-15d96a9f9b75",
        "timestamp": "2026-08-05T16:18:42.991244Z"
    }
}
 

Example response (422, Unknown preset):


{
    "status": "VALIDATION_ERROR",
    "message": "Unknown preset 'foo'."
}
 

Request      

PUT api/v1/admin/admins/{admin_id}/presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Body Parameters

presets   string[]     

List of admin preset keys. Pass [] to revoke all.

Get Scoped Admin Presets

requires authentication

Returns the preset keys currently active for a single admin in the given organisation context (union of global + org-scoped assignments). Omitting organization_id returns the same result as the global endpoint.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets?organization_id=018f1234-abcd-7000-0000-000000000001" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets"
);

const params = {
    "organization_id": "018f1234-abcd-7000-0000-000000000001",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'organization_id' => '018f1234-abcd-7000-0000-000000000001',
        ],
        'json' => [
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets')
      .replace(queryParameters: {
        'organization_id': '018f1234-abcd-7000-0000-000000000001',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "organization_id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "presets": [
            "super-admin"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "f5f405f3-eeec-4e79-abc8-7b8920e1b156",
        "timestamp": "2026-08-05T16:18:42.948008Z"
    }
}
 

Request      

GET api/v1/admin/admins/{admin_id}/scoped-presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Query Parameters

organization_id   string  optional    

optional UUID of the organisation to scope to. Example: 018f1234-abcd-7000-0000-000000000001

Body Parameters

organization_id   string  optional    

Must be a valid UUID. Must match an existing stored value. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Update Scoped Admin Presets

requires authentication

Bulk-replaces the org-scoped preset assignments for an admin. Only rows matching the supplied organization_id context are diffed; global assignments are never touched. Presets absent from the request body are revoked (soft); new ones are assigned. The diff and writes run inside a single DB transaction.

Required permission: admin.admins.manage (super-admin only by default).

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"018f1234-abcd-7000-0000-000000000001\",
    \"presets\": [
        \"admin\",
        \"admin-finance\"
    ]
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "018f1234-abcd-7000-0000-000000000001",
    "presets": [
        "admin",
        "admin-finance"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'organization_id' => '018f1234-abcd-7000-0000-000000000001',
            'presets' => ['admin', 'admin-finance'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/019f0593-c378-72f0-84b7-8adf1820207f/scoped-presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "018f1234-abcd-7000-0000-000000000001",
    "presets": [
        "admin",
        "admin-finance"
    ]
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Scoped admin presets updated.",
    "data": {
        "admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "organization_id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "presets": [
            "super-admin"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "dc0fa735-54c1-46d5-9457-cc38dd13b0e1",
        "timestamp": "2026-08-05T16:18:42.969201Z"
    }
}
 

Example response (422, Unknown preset):


{
    "status": "VALIDATION_ERROR",
    "message": "Unknown preset 'foo'."
}
 

Example response (422, Out of scope):


{
    "status": "VALIDATION_ERROR",
    "message": "Unknown preset 'co-admin'."
}
 

Request      

PUT api/v1/admin/admins/{admin_id}/scoped-presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

admin_id   string     

The ID of the admin. Example: 019f0593-c378-72f0-84b7-8adf1820207f

Body Parameters

organization_id   string     

UUID of the organisation to scope to. Example: 018f1234-abcd-7000-0000-000000000001

presets   string[]     

List of admin preset keys scoped to this org. Pass [] to revoke all scoped presets.

List Admin Presets

requires authentication

Returns the catalog of admin-scope presets (roles) that a super admin may assign to other admin accounts. Used by the admin dashboard's "Manage roles" dialog.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admin-presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admin-presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admin-presets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admin-presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "key": "super-admin",
            "label": "Super Admin",
            "description": "Unrestricted admin access including GDPR operations, app secrets, and admin account management.",
            "permissions": [
                "admin.admins.create",
                "admin.admins.deactivate",
                "admin.admins.reactivate",
                "admin.admins.reset-2fa",
                "admin.admins.update",
                "admin.admins.update-presets",
                "admin.admins.update-role",
                "admin.admins.view",
                "admin.app-settings.reveal-secret",
                "admin.app-settings.update",
                "admin.app-settings.view",
                "admin.assignments.manage",
                "admin.assignments.view",
                "admin.audit.view",
                "admin.billing.update",
                "admin.billing.view",
                "admin.compliance.aueg-monitor",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.contracts.manage",
                "admin.contracts.view",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.finance.manage-dunning",
                "admin.finance.manage-invoices",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.gdpr.anonymize",
                "admin.gdpr.export",
                "admin.gdpr.restrict",
                "admin.jobs.approve",
                "admin.jobs.emergency-close",
                "admin.jobs.emergency-reassign",
                "admin.jobs.manage-categories",
                "admin.jobs.manage-document-types",
                "admin.jobs.reject",
                "admin.jobs.view",
                "admin.legal.manage",
                "admin.legal.view",
                "admin.organizations.reactivate",
                "admin.organizations.reject",
                "admin.organizations.reset-password",
                "admin.organizations.review-documents",
                "admin.organizations.suspend",
                "admin.organizations.update",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.payroll.download-payslips",
                "admin.payroll.sync",
                "admin.payroll.trigger",
                "admin.payroll.view",
                "admin.ratings.moderate",
                "admin.ratings.view",
                "admin.reports.export",
                "admin.reports.view",
                "admin.security.detect-duplicates",
                "admin.security.manual-review",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.system.view-health",
                "admin.system.view-metrics",
                "admin.users.change-status",
                "admin.users.reset-password",
                "admin.users.update",
                "admin.users.update-by-admin",
                "admin.users.view"
            ],
            "category": "platform_admin",
            "highlights": [
                "presets.super-admin.highlights.full_access",
                "presets.super-admin.highlights.manage_admins",
                "presets.super-admin.highlights.platform_settings"
            ],
            "risk_level": "high"
        },
        {
            "key": "payroll-admin",
            "label": "Payroll Admin",
            "description": "Manage payroll calculations, payslip generation, and ELDA submissions.",
            "permissions": [
                "admin.contracts.view",
                "admin.employees.view",
                "admin.payroll.download-payslips",
                "admin.payroll.sync",
                "admin.payroll.trigger",
                "admin.payroll.view",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "compliance-officer",
            "label": "Compliance Officer",
            "description": "Employee/company document review, compliance monitoring (AÜG/AZG/ARG), audit log access.",
            "permissions": [
                "admin.audit.view",
                "admin.compliance.aueg-monitor",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.legal.manage",
                "admin.legal.view",
                "admin.organizations.reject",
                "admin.organizations.review-documents",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.security.detect-duplicates",
                "admin.security.manual-review"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "operations-manager",
            "label": "Operations Manager",
            "description": "Platform operations: employee/company review, support tickets, system health monitoring.",
            "permissions": [
                "admin.assignments.manage",
                "admin.assignments.view",
                "admin.employees.approve",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.view",
                "admin.jobs.approve",
                "admin.jobs.emergency-close",
                "admin.jobs.emergency-reassign",
                "admin.jobs.manage-categories",
                "admin.jobs.manage-document-types",
                "admin.jobs.reject",
                "admin.jobs.view",
                "admin.organizations.reject",
                "admin.organizations.review-documents",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.system.view-health",
                "admin.system.view-metrics"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "finance-admin",
            "label": "Finance Admin",
            "description": "Manage invoices, payments, and dunning processes.",
            "permissions": [
                "admin.billing.update",
                "admin.billing.view",
                "admin.finance.manage-dunning",
                "admin.finance.manage-invoices",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.reports.export",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "payroll-agent",
            "label": "Payroll Agent",
            "description": "View payroll status and download payslips. Cannot trigger runs or sync.",
            "permissions": [
                "admin.employees.view",
                "admin.payroll.download-payslips",
                "admin.payroll.view",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "compliance-agent",
            "label": "Compliance Agent",
            "description": "Monitor AZG/ARG warnings and review documents. Cannot verify/reject organisations.",
            "permissions": [
                "admin.audit.view",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.employees.review-documents",
                "admin.employees.view",
                "admin.organizations.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "support-supervisor",
            "label": "Support Supervisor",
            "description": "Full support-ticket control including assignment and escalation.",
            "permissions": [
                "admin.employees.view",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.users.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "ops-agent",
            "label": "Operations Agent",
            "description": "Review employee/company applications and monitor jobs. Cannot approve or reject.",
            "permissions": [
                "admin.assignments.view",
                "admin.employees.review-documents",
                "admin.employees.view",
                "admin.jobs.view",
                "admin.organizations.review-documents",
                "admin.organizations.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "finance-agent",
            "label": "Finance Agent",
            "description": "View invoices and payments. Cannot finalize invoices or manage dunning.",
            "permissions": [
                "admin.billing.view",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "support-agent",
            "label": "Support Agent",
            "description": "Handle support tickets, view user and organization information for context.",
            "permissions": [
                "admin.employees.view",
                "admin.organizations.view",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.users.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "admin",
            "label": "Admin",
            "description": "Standard admin: org/user management, audit log, settings. Use SRS roles instead.",
            "permissions": [
                "admin.admins.view",
                "admin.app-settings.update",
                "admin.app-settings.view",
                "admin.audit.view",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.organizations.reactivate",
                "admin.organizations.reject",
                "admin.organizations.reset-password",
                "admin.organizations.review-documents",
                "admin.organizations.suspend",
                "admin.organizations.update",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.users.change-status",
                "admin.users.reset-password",
                "admin.users.update",
                "admin.users.update-by-admin",
                "admin.users.view"
            ],
            "category": "platform_admin",
            "highlights": [
                "presets.admin.highlights.review_orgs",
                "presets.admin.highlights.moderate_campaigns",
                "presets.admin.highlights.approve_payouts",
                "presets.admin.highlights.no_admin_management"
            ],
            "risk_level": "high"
        },
        {
            "key": "moderator",
            "label": "Moderator",
            "description": "Read-only: view organisations, users, audit log. Use \"support-agent\" instead.",
            "permissions": [
                "admin.audit.view",
                "admin.employees.view",
                "admin.organizations.view",
                "admin.users.view"
            ],
            "category": "content",
            "highlights": [
                "presets.moderator.highlights.view_organizations",
                "presets.moderator.highlights.view_users",
                "presets.moderator.highlights.no_money_actions"
            ],
            "risk_level": "medium"
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "2762271f-0d07-4368-802a-e50e0d9f8403",
        "timestamp": "2026-08-05T16:18:43.032923Z"
    }
}
 

Request      

GET api/v1/admin/admin-presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Admin Presets

requires authentication

Returns the catalog of admin-scope presets (roles) that a super admin may assign to other admin accounts. Used by the admin dashboard's "Manage roles" dialog.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/acl/presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/acl/presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/acl/presets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/acl/presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "key": "super-admin",
            "label": "Super Admin",
            "description": "Unrestricted admin access including GDPR operations, app secrets, and admin account management.",
            "permissions": [
                "admin.admins.create",
                "admin.admins.deactivate",
                "admin.admins.reactivate",
                "admin.admins.reset-2fa",
                "admin.admins.update",
                "admin.admins.update-presets",
                "admin.admins.update-role",
                "admin.admins.view",
                "admin.app-settings.reveal-secret",
                "admin.app-settings.update",
                "admin.app-settings.view",
                "admin.assignments.manage",
                "admin.assignments.view",
                "admin.audit.view",
                "admin.billing.update",
                "admin.billing.view",
                "admin.compliance.aueg-monitor",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.contracts.manage",
                "admin.contracts.view",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.finance.manage-dunning",
                "admin.finance.manage-invoices",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.gdpr.anonymize",
                "admin.gdpr.export",
                "admin.gdpr.restrict",
                "admin.jobs.approve",
                "admin.jobs.emergency-close",
                "admin.jobs.emergency-reassign",
                "admin.jobs.manage-categories",
                "admin.jobs.manage-document-types",
                "admin.jobs.reject",
                "admin.jobs.view",
                "admin.legal.manage",
                "admin.legal.view",
                "admin.organizations.reactivate",
                "admin.organizations.reject",
                "admin.organizations.reset-password",
                "admin.organizations.review-documents",
                "admin.organizations.suspend",
                "admin.organizations.update",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.payroll.download-payslips",
                "admin.payroll.sync",
                "admin.payroll.trigger",
                "admin.payroll.view",
                "admin.ratings.moderate",
                "admin.ratings.view",
                "admin.reports.export",
                "admin.reports.view",
                "admin.security.detect-duplicates",
                "admin.security.manual-review",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.system.view-health",
                "admin.system.view-metrics",
                "admin.users.change-status",
                "admin.users.reset-password",
                "admin.users.update",
                "admin.users.update-by-admin",
                "admin.users.view"
            ],
            "category": "platform_admin",
            "highlights": [
                "presets.super-admin.highlights.full_access",
                "presets.super-admin.highlights.manage_admins",
                "presets.super-admin.highlights.platform_settings"
            ],
            "risk_level": "high"
        },
        {
            "key": "payroll-admin",
            "label": "Payroll Admin",
            "description": "Manage payroll calculations, payslip generation, and ELDA submissions.",
            "permissions": [
                "admin.contracts.view",
                "admin.employees.view",
                "admin.payroll.download-payslips",
                "admin.payroll.sync",
                "admin.payroll.trigger",
                "admin.payroll.view",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "compliance-officer",
            "label": "Compliance Officer",
            "description": "Employee/company document review, compliance monitoring (AÜG/AZG/ARG), audit log access.",
            "permissions": [
                "admin.audit.view",
                "admin.compliance.aueg-monitor",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.legal.manage",
                "admin.legal.view",
                "admin.organizations.reject",
                "admin.organizations.review-documents",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.security.detect-duplicates",
                "admin.security.manual-review"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "operations-manager",
            "label": "Operations Manager",
            "description": "Platform operations: employee/company review, support tickets, system health monitoring.",
            "permissions": [
                "admin.assignments.manage",
                "admin.assignments.view",
                "admin.employees.approve",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.view",
                "admin.jobs.approve",
                "admin.jobs.emergency-close",
                "admin.jobs.emergency-reassign",
                "admin.jobs.manage-categories",
                "admin.jobs.manage-document-types",
                "admin.jobs.reject",
                "admin.jobs.view",
                "admin.organizations.reject",
                "admin.organizations.review-documents",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.system.view-health",
                "admin.system.view-metrics"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "finance-admin",
            "label": "Finance Admin",
            "description": "Manage invoices, payments, and dunning processes.",
            "permissions": [
                "admin.billing.update",
                "admin.billing.view",
                "admin.finance.manage-dunning",
                "admin.finance.manage-invoices",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.reports.export",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "payroll-agent",
            "label": "Payroll Agent",
            "description": "View payroll status and download payslips. Cannot trigger runs or sync.",
            "permissions": [
                "admin.employees.view",
                "admin.payroll.download-payslips",
                "admin.payroll.view",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "compliance-agent",
            "label": "Compliance Agent",
            "description": "Monitor AZG/ARG warnings and review documents. Cannot verify/reject organisations.",
            "permissions": [
                "admin.audit.view",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.employees.review-documents",
                "admin.employees.view",
                "admin.organizations.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "support-supervisor",
            "label": "Support Supervisor",
            "description": "Full support-ticket control including assignment and escalation.",
            "permissions": [
                "admin.employees.view",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.users.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "ops-agent",
            "label": "Operations Agent",
            "description": "Review employee/company applications and monitor jobs. Cannot approve or reject.",
            "permissions": [
                "admin.assignments.view",
                "admin.employees.review-documents",
                "admin.employees.view",
                "admin.jobs.view",
                "admin.organizations.review-documents",
                "admin.organizations.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "finance-agent",
            "label": "Finance Agent",
            "description": "View invoices and payments. Cannot finalize invoices or manage dunning.",
            "permissions": [
                "admin.billing.view",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.reports.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "support-agent",
            "label": "Support Agent",
            "description": "Handle support tickets, view user and organization information for context.",
            "permissions": [
                "admin.employees.view",
                "admin.organizations.view",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.users.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "admin",
            "label": "Admin",
            "description": "Standard admin: org/user management, audit log, settings. Use SRS roles instead.",
            "permissions": [
                "admin.admins.view",
                "admin.app-settings.update",
                "admin.app-settings.view",
                "admin.audit.view",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.organizations.reactivate",
                "admin.organizations.reject",
                "admin.organizations.reset-password",
                "admin.organizations.review-documents",
                "admin.organizations.suspend",
                "admin.organizations.update",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.reports.view",
                "admin.users.change-status",
                "admin.users.reset-password",
                "admin.users.update",
                "admin.users.update-by-admin",
                "admin.users.view"
            ],
            "category": "platform_admin",
            "highlights": [
                "presets.admin.highlights.review_orgs",
                "presets.admin.highlights.moderate_campaigns",
                "presets.admin.highlights.approve_payouts",
                "presets.admin.highlights.no_admin_management"
            ],
            "risk_level": "high"
        },
        {
            "key": "moderator",
            "label": "Moderator",
            "description": "Read-only: view organisations, users, audit log. Use \"support-agent\" instead.",
            "permissions": [
                "admin.audit.view",
                "admin.employees.view",
                "admin.organizations.view",
                "admin.users.view"
            ],
            "category": "content",
            "highlights": [
                "presets.moderator.highlights.view_organizations",
                "presets.moderator.highlights.view_users",
                "presets.moderator.highlights.no_money_actions"
            ],
            "risk_level": "medium"
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "2762271f-0d07-4368-802a-e50e0d9f8403",
        "timestamp": "2026-08-05T16:18:43.032923Z"
    }
}
 

Request      

GET api/v1/admin/acl/presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Organizations

List Organizations

requires authentication

Returns a paginated list of all organisations, optionally filtered by a search term (matched against name and email) and/or a status value.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations?search=test&status=active&per_page=15&sort=name&order=asc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations"
);

const params = {
    "search": "test",
    "status": "active",
    "per_page": "15",
    "sort": "name",
    "order": "asc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'search' => 'test',
            'status' => 'active',
            'per_page' => '15',
            'sort' => 'name',
            'order' => 'asc',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations')
      .replace(queryParameters: {
        'search': 'test',
        'status': 'active',
        'per_page': '15',
        'sort': 'name',
        'order': 'asc',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "fe7a27ad-e2c9-4825-af4a-7df8fb6beaaf",
        "timestamp": "2026-08-05T16:18:42.087462Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 0
    }
}
 

Request      

GET api/v1/admin/organizations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

search   string  optional    

Search keyword. Example: test

status   string  optional    

Filter by status. Example: active

per_page   integer  optional    

Number of items per page. Example: 15

sort   string  optional    

Column to sort by. Example: name

order   string  optional    

"asc" or "desc". Example: asc

Export Organizations

requires authentication

Streams the full organisation list as a CSV file.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/export" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/export"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/export');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "09d5c681-e3a5-46ed-9a4d-9a3c9dbabc73",
        "timestamp": "2026-08-21T05:14:35.938874Z"
    }
}
 

Request      

GET api/v1/admin/organizations/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Returns the full profile of a single organisation, identified by

requires authentication

route-model binding. * @group Admin API

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Organisation retrieved successfully.",
    "data": {
        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "name": "Caritas",
        "slug": "caritas-4v1ues",
        "email": "caritas-portal@flexxr.eu.cc",
        "country_code": "+43",
        "phone_number": "6641234567",
        "contact_person_name": null,
        "contact_person_phone": null,
        "iso_country_code": "AT",
        "phone_e164": "+436641234567",
        "description": "Caritas test organisation used for end-to-end portal QA. Safe to delete.",
        "logo_url": null,
        "website": "https://www.caritas.at",
        "address": "Albrechtskreithgasse 19-21",
        "address_line2": null,
        "postal_code": null,
        "city": "Vienna",
        "country": "Austria",
        "status": "active",
        "verified_at": "2026-08-05T16:18:17.000000Z",
        "is_verified": true,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:17.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "gisa_status": "pending",
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "5f3a0146-cc1c-4a3d-832d-77df0c4cec1f",
        "timestamp": "2026-08-05T16:18:42.101496Z"
    }
}
 

Request      

GET api/v1/admin/organizations/{organization_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

Verify Organization

requires authentication

Verifies a pending organisation, transitioning it to Active status.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/verify" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/verify"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/verify';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/verify');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Organisation verified successfully.",
    "data": {
        "id": "019fd2b8-2abb-7347-a732-5c2c2ee4f03c",
        "name": "Feeney, Howell and Stehr",
        "slug": "feeney-howell-and-stehr-bfcdiz",
        "email": "gunnar90@hahn.com",
        "country_code": "+1",
        "phone_number": "2025550275",
        "contact_person_name": null,
        "contact_person_phone": null,
        "iso_country_code": null,
        "phone_e164": "+12025550275",
        "description": "Dolor aspernatur eligendi nam enim tempore. Possimus est unde quia dignissimos dolore unde eos. Est quos quam enim voluptatem ut molestiae.",
        "logo_url": null,
        "website": null,
        "address": null,
        "address_line2": null,
        "postal_code": null,
        "city": null,
        "country": null,
        "status": "active",
        "verified_at": "2026-08-05T16:18:42.000000Z",
        "is_verified": true,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:32.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "1e3ae433-1435-4a4b-8cd3-6c871aa8c9ea",
        "timestamp": "2026-08-05T16:18:42.117609Z"
    }
}
 

Request      

PUT api/v1/admin/organizations/{organization_id}/verify

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reject Organization

requires authentication

Rejects a pending organisation application.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reject';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Organisation rejected.",
    "data": {
        "id": "019fd2b8-2abb-7347-a732-5c2c2ee4f03c",
        "name": "Feeney, Howell and Stehr",
        "slug": "feeney-howell-and-stehr-bfcdiz",
        "email": "gunnar90@hahn.com",
        "country_code": "+1",
        "phone_number": "2025550275",
        "contact_person_name": null,
        "contact_person_phone": null,
        "iso_country_code": null,
        "phone_e164": "+12025550275",
        "description": "Dolor aspernatur eligendi nam enim tempore. Possimus est unde quia dignissimos dolore unde eos. Est quos quam enim voluptatem ut molestiae.",
        "logo_url": null,
        "website": null,
        "address": null,
        "address_line2": null,
        "postal_code": null,
        "city": null,
        "country": null,
        "status": "rejected",
        "verified_at": null,
        "is_verified": false,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:32.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "628391df-a753-4ffc-b5aa-8eb743ba49ba",
        "timestamp": "2026-08-05T16:18:42.132018Z"
    }
}
 

Request      

PUT api/v1/admin/organizations/{organization_id}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Suspends an active organisation and immediately revokes all active tokens.

requires authentication

Business-rule enforcement (active-only guard) is handled inside

OrganizationService::suspend(). * @group Admin API

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/suspend" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/suspend"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/suspend';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/suspend');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Organisation suspended successfully.",
    "data": {
        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "name": "Caritas",
        "slug": "caritas-4v1ues",
        "email": "caritas-portal@flexxr.eu.cc",
        "country_code": "+43",
        "phone_number": "6641234567",
        "contact_person_name": null,
        "contact_person_phone": null,
        "iso_country_code": "AT",
        "phone_e164": "+436641234567",
        "description": "Caritas test organisation used for end-to-end portal QA. Safe to delete.",
        "logo_url": null,
        "website": "https://www.caritas.at",
        "address": "Albrechtskreithgasse 19-21",
        "address_line2": null,
        "postal_code": null,
        "city": "Vienna",
        "country": "Austria",
        "status": "suspended",
        "verified_at": "2026-08-05T16:18:17.000000Z",
        "is_verified": true,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:17.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "55ed4c84-a8de-451b-a958-d4747f155cd0",
        "timestamp": "2026-08-05T16:18:42.146772Z"
    }
}
 

Request      

PUT api/v1/admin/organizations/{organization_id}/suspend

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Reactivates a suspended organisation, restoring its ability to operate.

requires authentication

The original verification metadata (verified_at, verified_by) is preserved unchanged. Business-rule enforcement (suspended-only guard) is handled inside

OrganizationService::reactivate(). * @group Admin API

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reactivate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reactivate';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/reactivate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Organisation reactivated successfully.",
    "data": {
        "id": "019fd2b8-2b93-70ff-9a0a-ef0bb511b558",
        "name": "Cormier and Sons",
        "slug": "cormier-and-sons-vnw4vm",
        "email": "wisozk.agustin@pacocha.org",
        "country_code": "+1",
        "phone_number": "2025550634",
        "contact_person_name": null,
        "contact_person_phone": null,
        "iso_country_code": null,
        "phone_e164": "+12025550634",
        "description": "Maxime omnis maxime autem aliquid voluptas magni consequatur nesciunt. Sapiente quam quae laudantium culpa reiciendis repellat dolores. Exercitationem expedita et eum modi.",
        "logo_url": null,
        "website": null,
        "address": null,
        "address_line2": null,
        "postal_code": null,
        "city": null,
        "country": null,
        "status": "active",
        "verified_at": null,
        "is_verified": false,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:32.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "c586e4b9-d8d2-4f0f-b3a1-d4339de77df8",
        "timestamp": "2026-08-05T16:18:42.159248Z"
    }
}
 

Request      

PUT api/v1/admin/organizations/{organization_id}/reactivate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

Updates mutable profile fields on an organisation as an admin.

requires authentication

Only the fields present in the request are applied; absent keys leave existing values untouched (PATCH semantics). Status transitions, bank-detail

changes, and verification metadata are handled by dedicated endpoints. * @group Admin API

Example request:
curl --request PATCH \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Beispieltext\",
    \"description\": \"Beispieltext\",
    \"logo_url\": \"Beispieltext\",
    \"website\": \"Beispieltext\",
    \"address\": \"Beispieltext\",
    \"address_line2\": \"Beispieltext\",
    \"postal_code\": \"AT\",
    \"city\": \"Beispieltext\",
    \"country\": \"Beispieltext\",
    \"iso_country_code\": \"AT\",
    \"platform_fee_flat_cents\": 1,
    \"country_code\": \"AT\",
    \"phone_number\": \"6641234567\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Beispieltext",
    "description": "Beispieltext",
    "logo_url": "Beispieltext",
    "website": "Beispieltext",
    "address": "Beispieltext",
    "address_line2": "Beispieltext",
    "postal_code": "AT",
    "city": "Beispieltext",
    "country": "Beispieltext",
    "iso_country_code": "AT",
    "platform_fee_flat_cents": 1,
    "country_code": "AT",
    "phone_number": "6641234567"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Beispieltext',
            'description' => 'Beispieltext',
            'logo_url' => 'Beispieltext',
            'website' => 'Beispieltext',
            'address' => 'Beispieltext',
            'address_line2' => 'Beispieltext',
            'postal_code' => 'AT',
            'city' => 'Beispieltext',
            'country' => 'Beispieltext',
            'iso_country_code' => 'AT',
            'platform_fee_flat_cents' => 1,
            'country_code' => 'AT',
            'phone_number' => '6641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Beispieltext",
    "description": "Beispieltext",
    "logo_url": "Beispieltext",
    "website": "Beispieltext",
    "address": "Beispieltext",
    "address_line2": "Beispieltext",
    "postal_code": "AT",
    "city": "Beispieltext",
    "country": "Beispieltext",
    "iso_country_code": "AT",
    "platform_fee_flat_cents": 1,
    "country_code": "AT",
    "phone_number": "6641234567"
};

  final response = await http.patch(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PATCH api/v1/admin/organizations/{organization_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

Body Parameters

name   string  optional    

Example: Beispieltext

description   string  optional    

Example: Beispieltext

logo_url   string  optional    

Example: Beispieltext

website   string  optional    

Example: Beispieltext

address   string  optional    

Example: Beispieltext

address_line2   string  optional    

Example: Beispieltext

postal_code   string  optional    

Example: AT

city   string  optional    

Example: Beispieltext

country   string  optional    

Example: Beispieltext

iso_country_code   string  optional    

Example: AT

platform_fee_flat_cents   integer  optional    

Example: 1

country_code   string     

Example: AT

phone_number   string     

Example: 6641234567

Admin-triggered organisation password reset code.

requires authentication

Dispatches the same 6-digit code the org would receive via the public /api/v1/org/auth/forgot-password endpoint — keeping the reset flow identical regardless of who initiated it. The admin never sees the code itself; the org follows the standard forgot-password → verify-code → reset-password path on the company portal.

Use case: an org owner who cannot log in (lost device, lost access to their inbox, etc.) phones Flexxr support, the operator confirms identity out-of-band, and then triggers this endpoint to push a fresh code to the org's primary email — without exposing the code to the operator.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/send-reset" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/send-reset"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/send-reset';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/send-reset');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password reset email sent.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "5fdc2a18-95f3-4f95-9f60-dd08aa90ef1a",
        "timestamp": "2026-08-05T16:18:42.182780Z"
    }
}
 

Request      

POST api/v1/admin/organizations/{organization_id}/send-reset

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

requires authentication

Stores a new logo file for the given organisation and returns the updated profile.

Lists the team members of a specific organisation for the admin detail page.

requires authentication

Phase-1 mirrors {@see ListOrgTeamAction}: the only seat is the organisation principal itself. The shape is frozen so the admin UI renders today and switches to multi-user mode the day the organization_members pivot lands.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/team" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/team"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/team';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/team');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "organization_id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "name": "Caritas",
            "email": "caritas-portal@flexxr.eu.cc",
            "role": "owner",
            "status": "active",
            "avatar_url": null,
            "is_principal": true,
            "joined_at": "2026-08-05T16:18:17+00:00",
            "last_active_at": "2026-08-05T16:18:33+00:00"
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "99d177f8-4686-4277-8f80-b2a436581fb7",
        "timestamp": "2026-08-05T16:18:42.213358Z"
    }
}
 

Request      

GET api/v1/admin/organizations/{organization_id}/team

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

Lists every team invitation (pending, accepted, revoked, expired) for a specific organisation. Powers the admin organisation detail page so support can see the invitation funnel without logging in as the org.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/invitations" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/invitations"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/invitations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/invitations');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "0c3a868f-3ae6-4436-836f-837c0d3bc51b",
        "timestamp": "2026-08-05T16:18:42.226051Z"
    }
}
 

Request      

GET api/v1/admin/organizations/{organization_id}/invitations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

List Organization Documents

requires authentication

Returns all KYC documents for a given organisation for admin review.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Dokumente erfolgreich abgerufen.",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "e0d196cb-4f23-427c-9c6a-50729a4b0ded",
        "timestamp": "2026-08-05T16:18:42.237423Z"
    }
}
 

Request      

GET api/v1/admin/organizations/{organization_id}/documents

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Download Organization Document

requires authentication

Streams an organisation's KYC document for admin review. Streaming works on any disk (local in dev, S3 in prod), unlike temporary signed URLs which the local driver cannot generate.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/download" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/download"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/download';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/download');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "970f18dd-d6d1-4695-8764-f8d8a246be22",
        "timestamp": "2026-08-21T05:14:36.064793Z"
    }
}
 

Request      

GET api/v1/admin/organizations/{organization_id}/documents/{document_id}/download

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

document_id   string     

The ID of the document. Example: 019f34d3-5029-7158-a549-910ae39abad5

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

document   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Approve Organization Document

requires authentication

Approves a KYC document submitted by an organisation.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/approve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/approve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/approve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/approve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/organizations/{organization_id}/documents/{document_id}/approve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

document_id   string     

The ID of the document. Example: 019f34d3-5029-7158-a549-910ae39abad5

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

document   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reject Organization Document

requires authentication

Rejects a KYC document submitted by an organisation, with a mandatory reason.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/documents/019f34d3-5029-7158-a549-910ae39abad5/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/organizations/{organization_id}/documents/{document_id}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

document_id   string     

The ID of the document. Example: 019f34d3-5029-7158-a549-910ae39abad5

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

document   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

List Organization Document Requests

requires authentication

Lists all document requests raised for a given organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Dokumentanforderungen erfolgreich abgerufen.",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "56675c8c-b427-4505-84a4-47ecf293ff7c",
        "timestamp": "2026-08-05T16:18:42.280175Z"
    }
}
 

Request      

GET api/v1/admin/organizations/{organization_id}/document-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Request Organization Documents

requires authentication

Raises a new document request for an organisation, optionally specifying a message and due date.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"document_types\": null,
    \"message\": \"Bitte um Rueckmeldung zur naechsten Schicht.\",
    \"due_at\": \"2026-09-30\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "document_types": null,
    "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "due_at": "2026-09-30"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'document_types' => null,
            'message' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
            'due_at' => '2026-09-30',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "document_types": null,
    "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "due_at": "2026-09-30"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/organizations/{organization_id}/document-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

document_types   string[]     
type   string     

Example: architecto

label   string     

Must not be greater than 200 characters. Example: n

required   boolean  optional    

Example: true

description   string  optional    

Must not be greater than 1000 characters. Example: Animi quos velit et fugiat.

message   string  optional    

Example: Bitte um Rueckmeldung zur naechsten Schicht.

due_at   date  optional    

Example: 2026-09-30

Cancel Organization Document Request

requires authentication

Withdraws an open document request before the organisation fulfils it.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests/019f37d2-a096-72da-ba50-ba5a5a9674fe" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests/019f37d2-a096-72da-ba50-ba5a5a9674fe"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests/019f37d2-a096-72da-ba50-ba5a5a9674fe';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/organizations/019f0593-c95d-73f3-aced-b962c9664f8d/document-requests/019f37d2-a096-72da-ba50-ba5a5a9674fe');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/admin/organizations/{organization_id}/document-requests/{documentRequest_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organization_id   string     

The ID of the organization. Example: 019f0593-c95d-73f3-aced-b962c9664f8d

documentRequest_id   string     

The ID of the documentRequest. Example: 019f37d2-a096-72da-ba50-ba5a5a9674fe

organization   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

documentRequest   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Employees

List Pending Employees

requires authentication

List employees pending approval.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/employees/pending" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/pending"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/pending';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/pending');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "employees": [
            {
                "id": "019fd2b8-29d1-7181-a321-e1d4deee8590",
                "user_id": "019fd2b8-29cf-7133-8ff2-ec12d31edb4d",
                "status": "pending_approval",
                "status_label": "Genehmigung ausstehend",
                "profile_completion_percentage": 100,
                "svs_number": "1706010190",
                "nationality": "AT",
                "city": "West Ignatius",
                "user": {
                    "id": "019fd2b8-29cf-7133-8ff2-ec12d31edb4d",
                    "name": "Chelsey Larkin",
                    "email": "umurphy@example.net",
                    "phone": null
                },
                "created_at": "2026-08-05 16:18",
                "updated_at": "2026-08-05 16:18"
            }
        ],
        "summary": {
            "pending_approval": 1,
            "correction_required": 0
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/admin/employees/pending

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Show Employee Review

requires authentication

Show an employee's profile for admin review.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/review" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/review"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/review';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/review');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "profile": {
            "id": "019fd2b7-f279-7142-a68d-0d4563a06070",
            "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
            "status": "active",
            "status_label": "Aktiv",
            "status_reason": null,
            "suspended_reason": null,
            "suspended_until": null,
            "profile_completion_percentage": 0,
            "svs_number": "5678200895",
            "date_of_birth": "1995-08-20",
            "place_of_birth": null,
            "nationality": "AT",
            "gender": null,
            "legal_first_name": null,
            "legal_last_name": null,
            "middle_names": null,
            "previous_last_names": null,
            "citizenship_primary": null,
            "citizenship_additional": null,
            "residence_status": null,
            "residence_status_label": null,
            "country_of_birth": null,
            "tax_id_set": false,
            "tax_number_set": false,
            "finanz_online_registered": false,
            "alternate_email": null,
            "street": "Neugasse 5",
            "house_number": null,
            "postal_code": "4020",
            "city": "Linz",
            "country": "AT",
            "correction_fields": null,
            "profile_picture_url": null,
            "preferred_radius_km": null,
            "bio": null,
            "marketing_consent": null,
            "gdpr_consent": null,
            "created_at": "2026-08-05 16:18",
            "updated_at": "2026-08-05 16:18"
        },
        "user": {
            "id": "019fd2b7-f271-73fd-8268-848be381e136",
            "first_name": "Anna",
            "last_name": "Neuling",
            "email": "pending@demo.flexxr.at",
            "phone": "+436769876543",
            "email_verified": true
        },
        "documents": [
            {
                "id": "019fd2b7-f280-73fe-83af-4fdd5f9db332",
                "type": "id_card",
                "type_label": "ID card",
                "filename": "ausweis.pdf",
                "file_url": "/storage/documents/demo/ausweis.pdf",
                "thumbnail_url": null,
                "status": "pending",
                "status_label": "Ausstehend",
                "rejection_reason": null,
                "expires_at": null,
                "is_expired": false,
                "virus_scanned_at": null,
                "ocr_name_match_score": null,
                "ocr_confidence": null,
                "needs_review": false,
                "discrepancies": [],
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-28ce-735c-aa32-c03c49655e69",
                "type": "passport",
                "type_label": "Passport",
                "filename": "veniam.pdf",
                "file_url": "/storage/employee/917d038a-c64d-30e4-8416-39aca59dc516/passport/2026/06/13/29374d0b-28b0-36d0-9ee2-bac06a7bb554.pdf",
                "thumbnail_url": null,
                "status": "verified",
                "status_label": "Verifiziert",
                "rejection_reason": null,
                "expires_at": "2033-05-22",
                "is_expired": false,
                "virus_scanned_at": null,
                "ocr_name_match_score": null,
                "ocr_confidence": null,
                "needs_review": false,
                "discrepancies": [],
                "created_at": "2026-08-05 16:18"
            }
        ],
        "qualifications": [],
        "bank_accounts": [],
        "emergency_contacts": [],
        "checklist": {
            "email_verified": true,
            "id_document_uploaded": true,
            "bank_account_added": false,
            "svs_number_provided": true,
            "profile_complete": false,
            "work_permit_valid": true
        },
        "work_permit": {
            "required": false,
            "expires_at": null,
            "is_expired": false,
            "days_until_expiry": null
        },
        "shifts": [
            {
                "assignment": {
                    "id": "019fd2b8-28d3-70d7-92eb-f1bb1577acaf",
                    "status": "signed",
                    "status_label": "Unterschrieben",
                    "shift_date": "2026-08-06",
                    "scheduled_start_time": "08:00:00",
                    "scheduled_end_time": "16:00:00",
                    "location_name": "Nolan-Roberts",
                    "location_address": "4309 O'Reilly Way Suite 012",
                    "hourly_rate_gross": "34.72",
                    "started_at": null,
                    "completed_at": null,
                    "job": {
                        "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                        "title": "Accountant"
                    },
                    "employee": {
                        "id": "019fd2b7-f271-73fd-8268-848be381e136",
                        "name": "Anna Neuling"
                    },
                    "contract": null,
                    "attendance": null,
                    "rating": null
                },
                "timeline": [
                    {
                        "event_type": "assignment.created",
                        "actor": "system",
                        "title": "Assignment created",
                        "description": "Assignment was created from job selection.",
                        "occurred_at": "2026-08-05T16:18:32+00:00",
                        "path": "/hours"
                    }
                ]
            },
            {
                "assignment": {
                    "id": "019fd2b8-28d8-728d-a447-8de1a42eec83",
                    "status": "checked_in",
                    "status_label": "Eingecheckt",
                    "shift_date": "2026-08-05",
                    "scheduled_start_time": "11:58:00",
                    "scheduled_end_time": "12:13:00",
                    "location_name": "Goldner and Sons",
                    "location_address": "30005 Prosacco Orchard",
                    "hourly_rate_gross": "18.57",
                    "started_at": null,
                    "completed_at": null,
                    "job": {
                        "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                        "title": "Accountant"
                    },
                    "employee": {
                        "id": "019fd2b7-f271-73fd-8268-848be381e136",
                        "name": "Anna Neuling"
                    },
                    "contract": null,
                    "attendance": null,
                    "rating": null
                },
                "timeline": [
                    {
                        "event_type": "assignment.created",
                        "actor": "system",
                        "title": "Assignment created",
                        "description": "Assignment was created from job selection.",
                        "occurred_at": "2026-08-05T16:18:32+00:00",
                        "path": "/hours"
                    }
                ]
            },
            {
                "assignment": {
                    "id": "019fd2b8-28d1-7112-b8e9-68bd79fbafb9",
                    "status": "awaiting_signature",
                    "status_label": "Unterschrift ausstehend",
                    "shift_date": "2026-08-04",
                    "scheduled_start_time": "16:57:00",
                    "scheduled_end_time": "09:21:00",
                    "location_name": "Buckridge-Collier",
                    "location_address": "661 Cayla Unions Suite 344",
                    "hourly_rate_gross": "27.68",
                    "started_at": null,
                    "completed_at": null,
                    "job": {
                        "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                        "title": "Accountant"
                    },
                    "employee": {
                        "id": "019fd2b7-f271-73fd-8268-848be381e136",
                        "name": "Anna Neuling"
                    },
                    "contract": null,
                    "attendance": null,
                    "rating": null
                },
                "timeline": [
                    {
                        "event_type": "assignment.created",
                        "actor": "system",
                        "title": "Assignment created",
                        "description": "Assignment was created from job selection.",
                        "occurred_at": "2026-08-05T16:18:32+00:00",
                        "path": "/hours"
                    }
                ]
            },
            {
                "assignment": {
                    "id": "019fd2b8-28df-7265-8b1e-cb5de026e4d7",
                    "status": "completed",
                    "status_label": "Abgeschlossen",
                    "shift_date": "2026-08-03",
                    "scheduled_start_time": "08:31:00",
                    "scheduled_end_time": "05:12:00",
                    "location_name": "Wehner Inc",
                    "location_address": "45222 Hansen Gardens",
                    "hourly_rate_gross": "18.25",
                    "started_at": null,
                    "completed_at": null,
                    "job": {
                        "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                        "title": "Accountant"
                    },
                    "employee": {
                        "id": "019fd2b7-f271-73fd-8268-848be381e136",
                        "name": "Anna Neuling"
                    },
                    "contract": null,
                    "attendance": null,
                    "rating": null
                },
                "timeline": [
                    {
                        "event_type": "assignment.created",
                        "actor": "system",
                        "title": "Assignment created",
                        "description": "Assignment was created from job selection.",
                        "occurred_at": "2026-08-05T16:18:32+00:00",
                        "path": "/hours"
                    }
                ]
            }
        ]
    }
}
 

Request      

GET api/v1/admin/employees/{profileId}/review

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Approve Employee

requires authentication

Approve an employee's profile.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"note\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'note' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "note": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The employee has been approved successfully.",
    "data": {
        "employee_id": "019fd2b8-29d1-7181-a321-e1d4deee8590",
        "status": "active"
    }
}
 

Request      

POST api/v1/admin/employees/{profileId}/approve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

note   string  optional    

Example: Beispieltext

Reject Employee

requires authentication

Reject an employee's application.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\",
    \"reason_code\": \"documents_invalid\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "reason_code": "documents_invalid"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
            'reason_code' => 'documents_invalid',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "reason_code": "documents_invalid"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The employee application has been rejected.",
    "data": {
        "employee_id": "019fd2b8-29d1-7181-a321-e1d4deee8590",
        "status": "rejected"
    }
}
 

Request      

POST api/v1/admin/employees/{profileId}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

reason_code   string     

Allowed: documents_invalid, identity_mismatch, sanctions_match, other. Example: documents_invalid

Request Correction

requires authentication

Request correction from employee before approval.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/request-correction" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\",
    \"fields_requiring_correction\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/request-correction"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "fields_requiring_correction": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/request-correction';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
            'fields_requiring_correction' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/019f9939-c4c3-70fb-a54e-5ebf63db36d2/request-correction');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "fields_requiring_correction": null
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Correction request has been sent to the employee.",
    "data": {
        "employee_id": "019fd2b8-29d1-7181-a321-e1d4deee8590",
        "status": "correction_required"
    }
}
 

Request      

POST api/v1/admin/employees/{profileId}/request-correction

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

fields_requiring_correction   string[]  optional    

Approve Document

requires authentication

Approve (verify) an employee document during admin review.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The document has been verified.",
    "data": {
        "document_id": "019fd2b8-6103-7134-bdc4-ac4ec15c8ab5",
        "status": "verified"
    }
}
 

Request      

POST api/v1/admin/employees/documents/{documentId}/approve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

documentId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reject Document

requires authentication

Reject an employee document during admin review. A reason is mandatory and is surfaced to the employee so they know what to fix.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The document has been rejected.",
    "data": {
        "document_id": "019fd2b8-6352-7026-9ee5-f2087fc25f75",
        "status": "rejected",
        "rejection_reason": "Das Dokument ist unleserlich. Bitte erneut hochladen."
    }
}
 

Request      

POST api/v1/admin/employees/documents/{documentId}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

documentId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Verify Qualification

requires authentication

Marks a worker's self-reported qualification as verified. Until an admin does this the qualification counts as the worker's own claim — the mobile app shows it without the verified badge.

An expired qualification cannot be verified: what would be confirmed is no longer in force.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/verify" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/verify"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/verify');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The qualification has been verified.",
    "data": {
        "qualification_id": "019fd41b-439a-727b-bdab-d37f8d7ca5f0",
        "is_verified": true
    }
}
 

Example response (422, Already expired):


{
    "status": "INVALID_OPERATION",
    "message": "Diese Qualifikation ist abgelaufen. Bitte erneuern Sie sie."
}
 

Request      

POST api/v1/admin/employees/qualifications/{qualificationId}/verify

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

qualificationId   string     

Identifier from the employee review. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reject Qualification

requires authentication

Refuses to verify a qualification — or withdraws a verification already given, when it turns out to have been wrong. The row stays on the worker's profile as their own claim; the reason is sent to them so they know what to fix.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Der Nachweis ist nicht lesbar.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Der Nachweis ist nicht lesbar."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Der Nachweis ist nicht lesbar.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/qualifications/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Der Nachweis ist nicht lesbar."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Verification of the qualification has been declined.",
    "data": {
        "qualification_id": "019fd41b-439a-727b-bdab-d37f8d7ca5f0",
        "is_verified": false
    }
}
 

Request      

POST api/v1/admin/employees/qualifications/{qualificationId}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

qualificationId   string     

Identifier from the employee review. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Why verification is refused, shown to the worker. Example: Der Nachweis ist nicht lesbar.

Jobs

List All Jobs

requires authentication

List all job listings across all organizations for admin oversight.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/jobs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/jobs"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/jobs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/jobs');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "jobs": [
            {
                "id": "019fd2b8-28e8-73a4-94d6-5a423c21f064",
                "title": "Foundry Mold and Coremaker",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "software",
                "category_label": "Software",
                "status": "draft",
                "status_label": "Entwurf",
                "status_color": "gray",
                "location_city": "South Zachery",
                "start_date": "2026-08-29",
                "end_date": "2026-09-25",
                "shift_start_time": "16:30:00",
                "shift_end_time": "14:33:00",
                "total_vacancies": 10,
                "filled_vacancies": 0,
                "hourly_rate_gross": 29.86,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:32+00:00"
            },
            {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "office",
                "category_label": "Büro",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "East Eliseo",
                "start_date": "2026-08-12",
                "end_date": "2026-08-19",
                "shift_start_time": "01:49:00",
                "shift_end_time": "18:57:00",
                "total_vacancies": 5,
                "filled_vacancies": 0,
                "hourly_rate_gross": 32.08,
                "applications_count": 1,
                "shifts_count": 4,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:32+00:00"
            },
            {
                "id": "019fd2b8-28ee-71e0-b50d-2c303c17f728",
                "title": "Kindergarten Teacher",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "warehouse",
                "category_label": "Lager",
                "status": "pending_review",
                "status_label": "In Prüfung",
                "status_color": "yellow",
                "location_city": "Eldahaven",
                "start_date": "2026-08-21",
                "end_date": "2026-09-15",
                "shift_start_time": "06:44:00",
                "shift_end_time": "06:04:00",
                "total_vacancies": 1,
                "filled_vacancies": 0,
                "hourly_rate_gross": 18.4,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:32+00:00"
            },
            {
                "id": "019fd2b8-2705-73fd-a401-002956f1f401",
                "title": "Fahrer*in Zustelldienst Wien Nord",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "logistics",
                "category_label": "Logistik",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-14",
                "end_date": "2026-08-15",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 4,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-2599-7355-a7f1-dcb185b05faa",
                "title": "Rezeptionist*in Hotel Sacher",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "hotel",
                "category_label": "Hotellerie",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-17",
                "end_date": "2026-08-19",
                "shift_start_time": "09:00:00",
                "shift_end_time": "17:00:00",
                "total_vacancies": 4,
                "filled_vacancies": 0,
                "hourly_rate_gross": 17.5,
                "applications_count": 51,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-24b0-700a-ae2a-142f5e48aead",
                "title": "Lagerhelfer*in Logistikzentrum Inzersdorf",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "warehouse",
                "category_label": "Lager",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-12",
                "end_date": "2026-08-14",
                "shift_start_time": "22:00:00",
                "shift_end_time": "06:00:00",
                "total_vacancies": 15,
                "filled_vacancies": 0,
                "hourly_rate_gross": 14.8,
                "applications_count": 29,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-270a-71f9-a13c-ef188105041c",
                "title": "Reinigungskraft Hotel Ibis Mariahilf",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "cleaning",
                "category_label": "Reinigung",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-16",
                "end_date": "2026-08-17",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 3,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13.2,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-26fb-7142-b3a3-df10c3591584",
                "title": "Küchenhilfe Streetfood Festival Naschmarkt",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "catering",
                "category_label": "Catering",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-13",
                "end_date": "2026-08-14",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 6,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-270f-715d-a3a0-76c4c314e3ff",
                "title": "Verkäufer*in Elektronik Mediamarkt Vösendorf",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "retail",
                "category_label": "Einzelhandel",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Vösendorf",
                "start_date": "2026-08-20",
                "end_date": "2026-08-21",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 7,
                "filled_vacancies": 0,
                "hourly_rate_gross": 14,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-2700-724b-adc5-2be8957e0590",
                "title": "Kassierer*in Supermarkt Floridsdorf",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "supermarket",
                "category_label": "Lebensmittelhandel",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-11",
                "end_date": "2026-08-12",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 5,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13.5,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:31+00:00"
            },
            {
                "id": "019fd2b8-21c8-72cc-80b1-47fd13450522",
                "title": "Barkeeper*in Sommernacht Open Air",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "gastro",
                "category_label": "Gastronomie",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-15",
                "end_date": "2026-08-17",
                "shift_start_time": "22:00:00",
                "shift_end_time": "06:00:00",
                "total_vacancies": 8,
                "filled_vacancies": 0,
                "hourly_rate_gross": 16,
                "applications_count": 38,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:30+00:00"
            },
            {
                "id": "019fd2b8-22cf-71c5-8edd-5e3ce9d29e8b",
                "title": "Promoter*in Vienna Marathon",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "event",
                "category_label": "Events",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-26",
                "end_date": "2026-08-28",
                "shift_start_time": "09:00:00",
                "shift_end_time": "17:00:00",
                "total_vacancies": 20,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13.5,
                "applications_count": 62,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:30+00:00"
            },
            {
                "id": "019fd2b8-1f86-7376-9a3a-c072ab5675cb",
                "title": "Service-Mitarbeiter*in Wiener Prater Festival",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "gastro",
                "category_label": "Gastronomie",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-19",
                "end_date": "2026-08-21",
                "shift_start_time": "09:00:00",
                "shift_end_time": "17:00:00",
                "total_vacancies": 12,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15.5,
                "applications_count": 45,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1dcd-734a-a041-f809f58e95fb",
                "title": "IT Supportkraft Vertretung",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "it_services",
                "category_label": "IT Services",
                "status": "pending_review",
                "status_label": "In Prüfung",
                "status_color": "yellow",
                "location_city": "Wien",
                "start_date": "2026-08-20",
                "end_date": "2026-09-04",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 2,
                "filled_vacancies": 0,
                "hourly_rate_gross": 22,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1dc8-7078-bcc2-07c7297ffa47",
                "title": "Nachtschicht Lager Q4",
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "status": "active"
                },
                "category": "logistics",
                "category_label": "Logistik",
                "status": "draft",
                "status_label": "Entwurf",
                "status_color": "gray",
                "location_city": "Graz",
                "start_date": "2026-12-03",
                "end_date": "2027-02-01",
                "shift_start_time": "22:00:00",
                "shift_end_time": "06:00:00",
                "total_vacancies": 20,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15.5,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1d92-7277-aa77-7172270002a5",
                "title": "Qualitätskontrolle Lebensmittel",
                "organization": {
                    "id": "019fd2b7-f4d3-71a1-89d4-ca7d5731b32a",
                    "name": "Wiener Gastro GmbH",
                    "status": "active"
                },
                "category": "production",
                "category_label": "Produktion",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Graz",
                "start_date": "2026-08-08",
                "end_date": "2026-10-04",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 4,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15.5,
                "applications_count": 8,
                "shifts_count": 2,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1dc6-72cc-acac-502231efcb6b",
                "title": "Messehostess Herbstmesse",
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "status": "active"
                },
                "category": "event",
                "category_label": "Events",
                "status": "draft",
                "status_label": "Entwurf",
                "status_color": "gray",
                "location_city": "Wien",
                "start_date": "2026-11-03",
                "end_date": "2026-11-08",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 10,
                "filled_vacancies": 0,
                "hourly_rate_gross": 14,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1dcb-73f4-9257-e7fa42fe8514",
                "title": "Sommerfest Catering",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas",
                    "status": "active"
                },
                "category": "catering",
                "category_label": "Catering",
                "status": "pending_review",
                "status_label": "In Prüfung",
                "status_color": "yellow",
                "location_city": "Wien",
                "start_date": "2026-09-24",
                "end_date": "2026-09-25",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 12,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15,
                "applications_count": 0,
                "shifts_count": 0,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1d1e-7188-9438-b0d6050c492a",
                "title": "Kursleiter*in Deutschkurs",
                "organization": {
                    "id": "019fd2b8-00e8-7186-8fa6-5b042a30b10d",
                    "name": "Transport & Spedition GmbH",
                    "status": "active"
                },
                "category": "education",
                "category_label": "Bildung",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-12",
                "end_date": "2026-11-03",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 3,
                "filled_vacancies": 0,
                "hourly_rate_gross": 18,
                "applications_count": 6,
                "shifts_count": 2,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1d4e-71d9-aad9-3d9f47a96e48",
                "title": "Ausstellungsführer Museum",
                "organization": {
                    "id": "019fd2b7-fbb4-73fa-b801-e0c8a8de2651",
                    "name": "Bauwerk GmbH",
                    "status": "active"
                },
                "category": "education",
                "category_label": "Bildung",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-15",
                "end_date": "2027-02-01",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 4,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15,
                "applications_count": 7,
                "shifts_count": 1,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1ce9-736a-9a61-22cea78283c9",
                "title": "Support-Techniker Außeneinsatz",
                "organization": {
                    "id": "019fd2b7-f91f-70cb-8409-8632db9df3c7",
                    "name": "Sicherheitsdienst Österreich",
                    "status": "active"
                },
                "category": "it_services",
                "category_label": "IT Services",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Graz",
                "start_date": "2026-08-12",
                "end_date": "2026-10-04",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 2,
                "filled_vacancies": 0,
                "hourly_rate_gross": 20,
                "applications_count": 4,
                "shifts_count": 2,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1d3f-7297-8384-35d386f1454b",
                "title": "Schulassistenz Volksschule",
                "organization": {
                    "id": "019fd2b7-f5ae-7390-a778-c08c8d56cdc0",
                    "name": "Event Solutions Austria",
                    "status": "active"
                },
                "category": "education",
                "category_label": "Bildung",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-08",
                "end_date": "2026-11-03",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 2,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15.5,
                "applications_count": 3,
                "shifts_count": 1,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1ccb-7322-8d33-448afd9b027b",
                "title": "Testingenieur Vertretung",
                "organization": {
                    "id": "019fd2b7-f842-71ba-a318-a919a24d4bcf",
                    "name": "Hotel & Spa Imperial",
                    "status": "active"
                },
                "category": "it_services",
                "category_label": "IT Services",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-15",
                "end_date": "2026-09-04",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 1,
                "filled_vacancies": 0,
                "hourly_rate_gross": 25,
                "applications_count": 6,
                "shifts_count": 1,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1dad-7375-b680-f51dcff486d0",
                "title": "Verpackungshelfer*in",
                "organization": {
                    "id": "019fd2b8-00e8-7186-8fa6-5b042a30b10d",
                    "name": "Transport & Spedition GmbH",
                    "status": "active"
                },
                "category": "production",
                "category_label": "Produktion",
                "status": "filled",
                "status_label": "Besetzt",
                "status_color": "purple",
                "location_city": "Wien",
                "start_date": "2026-07-29",
                "end_date": "2026-08-03",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 8,
                "filled_vacancies": 8,
                "hourly_rate_gross": 14.5,
                "applications_count": 4,
                "shifts_count": 1,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            },
            {
                "id": "019fd2b8-1d03-71ce-9e17-21e92ae7b9d7",
                "title": "Netzwerktechniker Hotline",
                "organization": {
                    "id": "019fd2b7-fbb4-73fa-b801-e0c8a8de2651",
                    "name": "Bauwerk GmbH",
                    "status": "active"
                },
                "category": "software",
                "category_label": "Software",
                "status": "filled",
                "status_label": "Besetzt",
                "status_color": "purple",
                "location_city": "Wien",
                "start_date": "2026-07-31",
                "end_date": "2026-08-03",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "total_vacancies": 3,
                "filled_vacancies": 3,
                "hourly_rate_gross": 21,
                "applications_count": 3,
                "shifts_count": 1,
                "has_compliance_warning": false,
                "is_deleted": false,
                "created_at": "2026-08-05T16:18:29+00:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 6,
            "per_page": 25,
            "total": 133
        }
    }
}
 

Request      

GET api/v1/admin/jobs

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Show Job

requires authentication

Show detailed job information for admin review.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "title": "Accountant",
            "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
            "category": "office",
            "category_label": "Büro",
            "status": "active",
            "status_label": "Aktiv",
            "status_color": "green",
            "shift_type": "night",
            "shift_type_label": "Nachtschicht",
            "organization": {
                "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                "name": "Caritas",
                "status": "active",
                "email": "caritas-portal@flexxr.eu.cc"
            },
            "location": {
                "name": "Barrows, Christiansen and Jones",
                "address": "586 Tremaine Row",
                "city": "East Eliseo",
                "postal_code": "10547-3697",
                "lat": 46.931383,
                "lng": 11.998284
            },
            "schedule": {
                "start_date": "2026-08-12",
                "end_date": "2026-08-19",
                "shift_start_time": "01:49:00",
                "shift_end_time": "18:57:00",
                "application_deadline": "2026-09-05",
                "confirmation_deadline": "2026-08-16",
                "sign_window_hours": 24
            },
            "vacancies": {
                "total": 5,
                "filled": 0,
                "available": 5
            },
            "compensation": {
                "hourly_rate_gross": 32.08,
                "supplements": {
                    "night": 0.7,
                    "weekend": 3.86
                },
                "estimated_total_gross": null
            },
            "requirements": {
                "qualifications": [],
                "dress_code": "Tempore necessitatibus quia illo suscipit.",
                "equipment_provided": "Adipisci fugiat doloremque atque consectetur necessitatibus sunt cumque.",
                "job_requirements": [
                    {
                        "type": "document",
                        "code": "passport",
                        "name": "Reisepass",
                        "is_mandatory": true
                    },
                    {
                        "type": "document",
                        "code": "driver_license",
                        "name": "Führerschein",
                        "is_mandatory": true
                    }
                ]
            },
            "contact": {
                "parking_info": null,
                "special_instructions": null
            },
            "review": {
                "submitted_at": null,
                "reviewed_at": null,
                "rejection_note": null,
                "submit_count": 0
            },
            "compliance": {
                "has_warning": false,
                "warnings": []
            },
            "timestamps": {
                "published_at": null,
                "filled_at": null,
                "cancelled_at": null,
                "deleted_at": null,
                "created_at": "2026-08-05T16:18:32+00:00",
                "updated_at": "2026-08-05T16:18:32+00:00"
            },
            "cancellation_reason": null,
            "is_deleted": false,
            "stats": {
                "applications_count": 1,
                "shifts_count": 4
            },
            "applications": [
                {
                    "id": "019fd2b8-28c6-73d1-b719-dae19c4d443c",
                    "user": {
                        "id": "019fd2b7-f271-73fd-8268-848be381e136",
                        "name": "Anna Neuling",
                        "email": "pending@demo.flexxr.at"
                    },
                    "status": "selected",
                    "applied_at": "2026-08-05T16:18:32+00:00"
                }
            ],
            "shifts": [
                {
                    "id": "019fd2b8-28df-7265-8b1e-cb5de026e4d7",
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "status": "completed",
                    "date": "2026-08-03",
                    "clock_in_at": null,
                    "clock_out_at": null
                },
                {
                    "id": "019fd2b8-28d1-7112-b8e9-68bd79fbafb9",
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "status": "awaiting_signature",
                    "date": "2026-08-04",
                    "clock_in_at": null,
                    "clock_out_at": null
                },
                {
                    "id": "019fd2b8-28d8-728d-a447-8de1a42eec83",
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "status": "checked_in",
                    "date": "2026-08-05",
                    "clock_in_at": null,
                    "clock_out_at": null
                },
                {
                    "id": "019fd2b8-28d3-70d7-92eb-f1bb1577acaf",
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "status": "signed",
                    "date": "2026-08-06",
                    "clock_in_at": null,
                    "clock_out_at": null
                }
            ],
            "planned_shifts": [
                {
                    "id": "019fd2b8-28d5-7103-b951-9f6ff1d94556",
                    "shift_date": "2026-08-05",
                    "start_time": "08:00",
                    "end_time": "16:00",
                    "break_minutes": 30,
                    "workers_needed": 2,
                    "shift_manager_name": null,
                    "notes": null,
                    "dress_code": null,
                    "contact_person_name": null,
                    "contact_person_phone": null,
                    "meeting_point": null,
                    "geofence": null,
                    "application_deadline": null
                }
            ]
        }
    }
}
 

Request      

GET api/v1/admin/jobs/{job}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

job   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Approve Job

requires authentication

POST /admin/jobs/{job}/approve

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/approve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been approved and published.",
    "data": {
        "id": "019fd2b8-6bb5-7113-b62c-7434d5f44733",
        "status": "active",
        "status_label": "Aktiv",
        "published_at": "2026-08-05 16:18"
    }
}
 

Request      

POST api/v1/admin/jobs/{job}/approve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

job   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reject Job

requires authentication

POST /admin/jobs/{job}/reject

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"note\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'note' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "note": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been rejected. The company can edit and resubmit it.",
    "data": {
        "id": "019fd2b8-70e9-7002-b15b-8db7f766386d",
        "status": "rejected",
        "status_label": "Abgelehnt",
        "rejection_note": "Der Stundensatz liegt unter dem Kollektivvertrag."
    }
}
 

Request      

POST api/v1/admin/jobs/{job}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

job   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

note   string     

Example: Beispieltext

Force Close Job

requires authentication

Emergency close a job listing by admin.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/force-close" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\",
    \"notify_employees\": true,
    \"notify_organization\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/force-close"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "notify_employees": true,
    "notify_organization": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/force-close';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
            'notify_employees' => true,
            'notify_organization' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/force-close');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "notify_employees": true,
    "notify_organization": true
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "messages.job_force_closed",
    "data": {
        "job_id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
        "affected_shifts_count": 2,
        "affected_shifts": [
            {
                "shift_id": "019fd2b8-28d1-7112-b8e9-68bd79fbafb9",
                "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                "user_email": "pending@demo.flexxr.at"
            },
            {
                "shift_id": "019fd2b8-28d3-70d7-92eb-f1bb1577acaf",
                "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                "user_email": "pending@demo.flexxr.at"
            }
        ]
    }
}
 

Request      

POST api/v1/admin/jobs/{job}/force-close

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

job   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

notify_employees   boolean  optional    

Example: true

notify_organization   boolean  optional    

Example: true

requires authentication

Sets or flips whether a job is featured on the home screen. Pass is_featured to set an explicit value, or omit it to flip the current value.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f1584-1555-72af-96c3-88f3f979d25c/featured" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"is_featured\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/jobs/019f1584-1555-72af-96c3-88f3f979d25c/featured"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "is_featured": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/jobs/019f1584-1555-72af-96c3-88f3f979d25c/featured';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'is_featured' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/jobs/019f1584-1555-72af-96c3-88f3f979d25c/featured');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "is_featured": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Settings

List Settings

requires authentication

Returns all application settings, optionally filtered by group.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/settings?group=general&per_page=15" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings"
);

const params = {
    "group": "general",
    "per_page": "15",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'group' => 'general',
            'per_page' => '15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings')
      .replace(queryParameters: {
        'group': 'general',
        'per_page': '15',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "4e6a4f9a-ecc4-4ca9-b4ea-292f901c07e7",
        "timestamp": "2026-08-05T16:18:42.512221Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 0
    }
}
 

Request      

GET api/v1/admin/settings

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

group   string  optional    

Filter by group. Example: general

per_page   integer  optional    

Number of items per page. Example: 15

Bulk-update application settings in a single transactional request.

requires authentication

Used by the admin Settings page to save a whole tab (e.g. "Stripe", "Mail") at once. Per-row payloads are individually validated against each setting's validation rules, and the entire batch is rolled back if any row fails authorisation or validation.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/settings" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"settings\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "settings": null
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'settings' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "settings": null
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/admin/settings

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

settings   string[]     
key   string     

Must match an existing stored value. Must not be greater than 191 characters. Example: b

value   string  optional    

Bulk-update application settings in a single transactional request.

requires authentication

Used by the admin Settings page to save a whole tab (e.g. "Stripe", "Mail") at once. Per-row payloads are individually validated against each setting's validation rules, and the entire batch is rolled back if any row fails authorisation or validation.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/settings/bulk" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"settings\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings/bulk"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "settings": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings/bulk';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'settings' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings/bulk');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "settings": null
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/settings/bulk

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

settings   string[]     
key   string     

Must match an existing stored value. Must not be greater than 191 characters. Example: b

value   string  optional    

Reveal the cleartext value of a secret setting.

requires authentication

Super-admin only; every reveal is audit-logged with admin id, ip, user-agent, and timestamp. Clients should treat the returned value as one-shot — re-displaying it requires another reveal call.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Setting value revealed.",
    "data": {
        "key": "app.name",
        "value": "Flexxer Backend"
    },
    "errors": null,
    "meta": {
        "request_id": "df0d0749-d201-42cf-aa40-f250f25d2b49",
        "timestamp": "2026-08-05T16:18:42.552956Z"
    }
}
 

Request      

POST api/v1/admin/settings/{setting_key}/reveal

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

setting_key   string     

Example: app.name

Reveal the cleartext value of a secret setting.

requires authentication

Super-admin only; every reveal is audit-logged with admin id, ip, user-agent, and timestamp. Clients should treat the returned value as one-shot — re-displaying it requires another reveal call.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings/app.name/reveal');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Setting value revealed.",
    "data": {
        "key": "app.name",
        "value": "Flexxer Backend"
    },
    "errors": null,
    "meta": {
        "request_id": "df0d0749-d201-42cf-aa40-f250f25d2b49",
        "timestamp": "2026-08-05T16:18:42.552956Z"
    }
}
 

Request      

GET api/v1/admin/settings/{setting_key}/reveal

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

setting_key   string     

Example: app.name

Returns a single application setting identified by its key via

requires authentication

route-model binding ({setting:key}). * @group Admin API

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings/app.name';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings/app.name');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Setting retrieved successfully.",
    "data": {
        "id": "019fd2b7-eea2-7183-91ff-569d302b80a5",
        "key": "app.name",
        "value": "Flexxer Backend",
        "has_value": true,
        "type": "string",
        "group": "application",
        "label": "Application name",
        "description": null,
        "is_secret": false,
        "is_encrypted": false,
        "is_public": true,
        "config_path": "app.name",
        "validation": [
            "max:100"
        ],
        "sort_order": 10
    },
    "errors": null,
    "meta": {
        "request_id": "6bc74203-0d37-4005-acc7-4ca85f5ae75b",
        "timestamp": "2026-08-05T16:18:42.572305Z"
    }
}
 

Request      

GET api/v1/admin/settings/{setting_key}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

setting_key   string     

Example: app.name

Update a single application setting identified by its key via route-model binding ({setting:key}).

requires authentication

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"value\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "value": "Beispieltext"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings/app.name';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'value' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings/app.name');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "value": "Beispieltext"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Setting updated successfully.",
    "data": {
        "id": "019fd2b7-eea2-7183-91ff-569d302b80a5",
        "key": "app.name",
        "value": "Beispieltext",
        "has_value": true,
        "type": "string",
        "group": "application",
        "label": "Application name",
        "description": null,
        "is_secret": false,
        "is_encrypted": false,
        "is_public": true,
        "config_path": "app.name",
        "validation": [
            "max:100"
        ],
        "sort_order": 10
    },
    "errors": null,
    "meta": {
        "request_id": "0d7997b1-efd3-4632-bc1f-9579037fc828",
        "timestamp": "2026-08-05T16:18:42.584365Z"
    }
}
 

Request      

PUT api/v1/admin/settings/{setting_key}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

setting_key   string     

Example: app.name

Body Parameters

value   string  optional    

Example: Beispieltext

Update a single application setting identified by its key via route-model binding ({setting:key}).

requires authentication

Example request:
curl --request PATCH \
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"value\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/settings/app.name"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "value": "Beispieltext"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/settings/app.name';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'value' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/settings/app.name');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "value": "Beispieltext"
};

  final response = await http.patch(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Setting updated successfully.",
    "data": {
        "id": "019fd2b7-eea2-7183-91ff-569d302b80a5",
        "key": "app.name",
        "value": "Beispieltext",
        "has_value": true,
        "type": "string",
        "group": "application",
        "label": "Application name",
        "description": null,
        "is_secret": false,
        "is_encrypted": false,
        "is_public": true,
        "config_path": "app.name",
        "validation": [
            "max:100"
        ],
        "sort_order": 10
    },
    "errors": null,
    "meta": {
        "request_id": "0d7997b1-efd3-4632-bc1f-9579037fc828",
        "timestamp": "2026-08-05T16:18:42.584365Z"
    }
}
 

Request      

PATCH api/v1/admin/settings/{setting_key}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

setting_key   string     

Example: app.name

Body Parameters

value   string  optional    

Example: Beispieltext

Audit Logs

Shortcut endpoint that returns the audit history for a specific mobile user. Equivalent to calling ListAuditLogsAction with

requires authentication

actor_type=User and actor_id={user}, but more ergonomic. * @group Admin API

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/audit-logs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/audit-logs"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/audit-logs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/audit-logs');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "986148e1-fff0-482e-aaa0-5d41090efc2f",
        "timestamp": "2026-08-05T16:18:41.836956Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 25,
        "total": 0
    }
}
 

Request      

GET api/v1/admin/users/{user_id}/audit-logs

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Lists audit logs with filters on actor, event, IP, and date range.

requires authentication

Results are paginated and sorted by occurred_at descending so the

admin UI shows the most recent events first. * @group Admin API

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/audit-logs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/audit-logs"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/audit-logs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/audit-logs');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "id": "019fd2b7-f16f-722a-a17a-a0e1db7deb06",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeBankAccount",
            "subject_id": "019fd2b7-f16e-704c-b219-414a49a200c8",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "is_primary": true,
                    "iban": "***MASKED***",
                    "bic": "RLNWATWW",
                    "bank_name": "Raiffeisen Bank",
                    "account_holder": "M*************",
                    "verified_at": "2026-03-05 16:18:18",
                    "is_verified": true,
                    "id": "019fd2b7-f16e-704c-b219-414a49a200c8",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f174-723d-9443-de112a7d144a",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeDocument",
            "subject_id": "019fd2b7-f173-70ee-8312-3dfc81345d78",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "type": "id_card",
                    "original_filename": "p*****************f",
                    "storage_path": "c0ecb9801dd5592ab1d5b796b45cf183ae7a3b2a00154bd87cd0d0f39f71b787",
                    "file_size": 306900,
                    "mime_type": "application/pdf",
                    "status": "verified",
                    "document_number": "P******6",
                    "expires_at": "2031-08-05 16:18:18",
                    "reviewed_at": "2026-02-05 16:18:18",
                    "id": "019fd2b7-f173-70ee-8312-3dfc81345d78",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f177-7063-a530-e7b1cbb65fe0",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeDocument",
            "subject_id": "019fd2b7-f176-71df-a915-724d63fbbf7b",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "type": "residence_permit",
                    "original_filename": "a******************f",
                    "storage_path": "a1a163d574e16f564d36030b921157863ae59a9359cc46f01fc02abaebafac5f",
                    "file_size": 158747,
                    "mime_type": "application/pdf",
                    "status": "verified",
                    "document_number": "A***********5",
                    "expires_at": "2028-08-05 16:18:18",
                    "reviewed_at": "2026-02-05 16:18:18",
                    "id": "019fd2b7-f176-71df-a915-724d63fbbf7b",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f17b-71b8-8ce8-d07f4bac6cb2",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeDocument",
            "subject_id": "019fd2b7-f17a-711b-a732-08f306e90713",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "type": "social_insurance_card",
                    "original_filename": "e********f",
                    "storage_path": "bd958714cff13dfbfeb1e81751a841391dfe7cfb351ea518bf39f9b74b6a8751",
                    "file_size": 118449,
                    "mime_type": "application/pdf",
                    "status": "verified",
                    "document_number": null,
                    "expires_at": null,
                    "reviewed_at": "2026-02-05 16:18:18",
                    "id": "019fd2b7-f17a-711b-a732-08f306e90713",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f180-7174-a1df-07c27ea86e09",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeQualification",
            "subject_id": "019fd2b7-f17e-71c4-9e83-3bfb364ee6f5",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "name": "Lebensmittelhygiene-Schulung",
                    "description": "HACCP-konform",
                    "issuing_organization": "Wifi Wien",
                    "credential_id": "CERT-VSMQAWJD",
                    "issued_at": "2024-08-05 16:18:18",
                    "expires_at": "2029-08-05 16:18:18",
                    "is_verified": true,
                    "verified_at": "2026-03-05 16:18:18",
                    "id": "019fd2b7-f17e-71c4-9e83-3bfb364ee6f5",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f183-72de-8fa6-ba8cb8c97a5e",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeQualification",
            "subject_id": "019fd2b7-f182-7351-9c68-f8069cc2b654",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "name": "Erste-Hilfe-Kurs (16h)",
                    "description": "Betrieblicher Ersthelfer",
                    "issuing_organization": "Rotes Kreuz",
                    "credential_id": "CERT-0GX09TN6",
                    "issued_at": "2025-08-05 16:18:18",
                    "expires_at": "2027-08-05 16:18:18",
                    "is_verified": true,
                    "verified_at": "2026-03-05 16:18:18",
                    "id": "019fd2b7-f182-7351-9c68-f8069cc2b654",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f186-7171-9186-ecc5291b5720",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeQualification",
            "subject_id": "019fd2b7-f184-705f-902c-e3fe336e4696",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "name": "Führerschein Klasse B",
                    "description": "PKW",
                    "issuing_organization": "Magistrat Wien",
                    "credential_id": "CERT-MMURVRZ9",
                    "issued_at": "2018-08-05 16:18:18",
                    "expires_at": "2033-08-05 16:18:18",
                    "is_verified": true,
                    "verified_at": "2026-03-05 16:18:18",
                    "id": "019fd2b7-f184-705f-902c-e3fe336e4696",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f18a-7051-a1a0-f69915c7b971",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\LegalConsent",
            "subject_id": "019fd2b7-f189-70a8-90e2-a0cd3bea33f9",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "consent_type": "terms_of_service",
                    "version": "1.0",
                    "is_granted": true,
                    "ip_address": "192.168.1.1",
                    "user_agent": "Mozilla/5.0 Flexxr/1.0",
                    "id": "019fd2b7-f189-70a8-90e2-a0cd3bea33f9"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f18f-713a-8653-ba9b92ae4893",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\LegalConsent",
            "subject_id": "019fd2b7-f18d-7173-b3c9-a930482056cc",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "consent_type": "privacy_policy",
                    "version": "1.0",
                    "is_granted": true,
                    "ip_address": "192.168.1.1",
                    "user_agent": "Mozilla/5.0 Flexxr/1.0",
                    "id": "019fd2b7-f18d-7173-b3c9-a930482056cc"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f193-730e-9ea9-dc287d5a2138",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\LegalConsent",
            "subject_id": "019fd2b7-f192-70a0-ad38-e7249a9072e9",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "consent_type": "data_processing",
                    "version": "1.0",
                    "is_granted": true,
                    "ip_address": "192.168.1.1",
                    "user_agent": "Mozilla/5.0 Flexxr/1.0",
                    "id": "019fd2b7-f192-70a0-ad38-e7249a9072e9"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f196-7342-8b80-22a97ced07f8",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\LegalConsent",
            "subject_id": "019fd2b7-f195-70c4-8789-2a049d2c90e0",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "consent_type": "framework_contract",
                    "version": "1.0",
                    "is_granted": true,
                    "ip_address": "192.168.1.1",
                    "user_agent": "Mozilla/5.0 Flexxr/1.0",
                    "id": "019fd2b7-f195-70c4-8789-2a049d2c90e0"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f27c-72ac-b5bb-02c51b40af7b",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeProfile",
            "subject_id": "019fd2b7-f279-7142-a68d-0d4563a06070",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "status": "pending_approval",
                    "date_of_birth": "1*****************0",
                    "nationality": "AT",
                    "address_line_1": "***REDACTED***",
                    "postal_code": "4**0",
                    "city": "Linz",
                    "country": "AT",
                    "svs_number": "***MASKED***",
                    "id": "019fd2b7-f279-7142-a68d-0d4563a06070",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f283-701d-a54c-158235363ec0",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeDocument",
            "subject_id": "019fd2b7-f280-73fe-83af-4fdd5f9db332",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "type": "id_card",
                    "original_filename": "a*********f",
                    "storage_path": "9155853482e609f840bdc816663625331d19feca66fdea30517b2e836a5078f8",
                    "file_size": 245000,
                    "mime_type": "application/pdf",
                    "status": "pending",
                    "id": "019fd2b7-f280-73fe-83af-4fdd5f9db332",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f288-7040-a457-b028861470d3",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\LegalConsent",
            "subject_id": "019fd2b7-f286-701b-a4b6-2c2aff8e2daf",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "consent_type": "terms_of_service",
                    "version": "1.0",
                    "is_granted": true,
                    "ip_address": "192.168.1.100",
                    "id": "019fd2b7-f286-701b-a4b6-2c2aff8e2daf"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f369-7004-afff-8a69186dc602",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeProfile",
            "subject_id": "019fd2b7-f367-70e0-95e4-16a395c2b4ec",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f362-705a-bd7d-66b2f8394aa2",
                    "status": "suspended",
                    "date_of_birth": "1*****************0",
                    "nationality": "AT",
                    "address_line_1": "***REDACTED***",
                    "postal_code": "1**0",
                    "city": "Wien",
                    "country": "AT",
                    "suspended_reason": "Wiederholte No-Shows bei bestätigten Schichten",
                    "id": "019fd2b7-f367-70e0-95e4-16a395c2b4ec",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f373-73e1-9170-f39694b52605",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f371-732c-8cdb-6de13be42891",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "gastro",
                    "name": "Gastronomie",
                    "name_en": "Gastronomy",
                    "icon": "utensils",
                    "kollektivvertrag": "Gastgewerbe",
                    "sort_order": 1,
                    "is_active": true,
                    "is_featured": true,
                    "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f375-724e-84b8-4f964842107a",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "hotel",
                    "name": "Hotellerie",
                    "name_en": "Hospitality",
                    "icon": "hotel",
                    "kollektivvertrag": "Hotel- und Gastgewerbe",
                    "sort_order": 2,
                    "is_active": true,
                    "is_featured": true,
                    "id": "019fd2b7-f375-724e-84b8-4f9647ef6419",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f378-72e4-b49e-cfb1fc2a49b0",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f377-7108-8400-efa89715a39f",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "food_service",
                    "name": "Food Service",
                    "name_en": "Food Service",
                    "icon": "utensils",
                    "kollektivvertrag": "Gastgewerbe",
                    "sort_order": 3,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f377-7108-8400-efa89715a39f",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f37b-70a0-9864-944852d41e9f",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f37a-72a9-84a9-7862f636e2fd",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "catering",
                    "name": "Catering",
                    "name_en": "Catering",
                    "icon": "utensils",
                    "kollektivvertrag": "Gastgewerbe",
                    "sort_order": 4,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f37a-72a9-84a9-7862f636e2fd",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f37e-73e1-8bb1-161448fd23cd",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f37d-712d-92b3-aa5540f6b2a7",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "retail",
                    "name": "Einzelhandel",
                    "name_en": "Retail",
                    "icon": "shopping-cart",
                    "kollektivvertrag": "Handel",
                    "sort_order": 5,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f37d-712d-92b3-aa5540f6b2a7",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f381-70ee-b46d-57a550a4104d",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f380-7300-91c6-75c968e6d045",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "supermarket",
                    "name": "Lebensmittelhandel",
                    "name_en": "Grocery",
                    "icon": "shopping-cart",
                    "kollektivvertrag": "Handel",
                    "sort_order": 6,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f380-7300-91c6-75c968e6d045",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f385-7167-a529-4245dd301d44",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f384-7071-b096-674d16b4ad00",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "pharmacy",
                    "name": "Apotheke",
                    "name_en": "Pharmacy",
                    "icon": "heart-pulse",
                    "kollektivvertrag": "Pharmazeutischer Großhandel",
                    "sort_order": 7,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f384-7071-b096-674d16b4ad00",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f389-7022-8c5e-bf2ea9173fe3",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f388-7098-b463-ae170338e60f",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "production",
                    "name": "Produktion",
                    "name_en": "Production",
                    "icon": "industry",
                    "kollektivvertrag": "Industrie",
                    "sort_order": 8,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f388-7098-b463-ae170338e60f",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f38c-7393-918e-19b1ae541bf2",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Job\\Models\\JobCategory",
            "subject_id": "019fd2b7-f38b-7209-8b83-b345b172def2",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "slug": "warehouse",
                    "name": "Lager",
                    "name_en": "Warehouse",
                    "icon": "industry",
                    "kollektivvertrag": "Industrie",
                    "sort_order": 9,
                    "is_active": true,
                    "is_featured": false,
                    "id": "019fd2b7-f38b-7209-8b83-b345b172def2",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        },
        {
            "id": "019fd2b7-f165-7231-8be6-f3c0c9d9acd0",
            "event": "model.created",
            "actor_type": null,
            "actor_id": null,
            "actor_guard": null,
            "subject_type": "App\\Domain\\Employee\\Models\\EmployeeProfile",
            "subject_id": "019fd2b7-f161-73ae-875c-d74efc63d8bb",
            "ip_address": "127.0.0.1",
            "user_agent": "Symfony",
            "session_token_id": null,
            "metadata": {
                "old_values": null,
                "new_values": {
                    "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                    "status": "active",
                    "date_of_birth": "1*****************0",
                    "nationality": "AT",
                    "address_line_1": "***REDACTED***",
                    "address_line_2": "***REDACTED***",
                    "postal_code": "1**0",
                    "city": "Wien",
                    "country": "AT",
                    "svs_number": "***MASKED***",
                    "tax_id": "e******************************************************************************************************************************************************************************************************9",
                    "emergency_contact_name": "M***************",
                    "emergency_contact_phone": "********5678",
                    "emergency_contact_relationship": "Ehefrau",
                    "approved_at": "2026-02-05 16:18:18",
                    "id": "019fd2b7-f161-73ae-875c-d74efc63d8bb",
                    "updated_at": "2026-08-05 16:18:18",
                    "created_at": "2026-08-05 16:18:18"
                },
                "correlation_id": null
            },
            "occurred_at": "2026-08-05T16:18:17.000000Z"
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "af080157-9260-4732-8976-5e113b9c26d2",
        "timestamp": "2026-08-05T16:18:42.488489Z",
        "current_page": 1,
        "last_page": 72,
        "per_page": 25,
        "total": 1790
    }
}
 

Request      

GET api/v1/admin/audit-logs

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Export Audit Logs

requires authentication

Streams audit logs as a CSV download, honoring the same filters as the list endpoint.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/audit-logs/export?actor_type=App%5C%5CDomain%5C%5CUser%5C%5CModels%5C%5CUser&actor_id=9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e&subject_type=App%5C%5CDomain%5C%5COrganization%5C%5CModels%5C%5COrganization&subject_id=9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e&event=login.failed&from=2026-04-01T00%3A00%3A00Z&to=2026-04-30T23%3A59%3A59Z&ip_address=203.0.113.42" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/audit-logs/export"
);

const params = {
    "actor_type": "App\\Domain\\User\\Models\\User",
    "actor_id": "9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e",
    "subject_type": "App\\Domain\\Organization\\Models\\Organization",
    "subject_id": "9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e",
    "event": "login.failed",
    "from": "2026-04-01T00:00:00Z",
    "to": "2026-04-30T23:59:59Z",
    "ip_address": "203.0.113.42",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/audit-logs/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'actor_type' => 'App\\Domain\\User\\Models\\User',
            'actor_id' => '9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e',
            'subject_type' => 'App\\Domain\\Organization\\Models\\Organization',
            'subject_id' => '9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e',
            'event' => 'login.failed',
            'from' => '2026-04-01T00:00:00Z',
            'to' => '2026-04-30T23:59:59Z',
            'ip_address' => '203.0.113.42',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/audit-logs/export')
      .replace(queryParameters: {
        'actor_type': 'App\\Domain\\User\\Models\\User',
        'actor_id': '9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e',
        'subject_type': 'App\\Domain\\Organization\\Models\\Organization',
        'subject_id': '9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e',
        'event': 'login.failed',
        'from': '2026-04-01T00:00:00Z',
        'to': '2026-04-30T23:59:59Z',
        'ip_address': '203.0.113.42',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "6e0665b2-afbf-4bce-b235-2956571a8731",
        "timestamp": "2026-08-21T05:14:36.328392Z"
    }
}
 

Request      

GET api/v1/admin/audit-logs/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

actor_type   string  optional    

Filter by actor class. Example: App\\Domain\\User\\Models\\User

actor_id   string  optional    

Filter by actor id. Example: 9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e

subject_type   string  optional    

Filter by subject class. Example: App\\Domain\\Organization\\Models\\Organization

subject_id   string  optional    

Filter by subject id (UUID). Example: 9b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e

event   string  optional    

Filter by AuditEvent value. Example: login.failed

from   string  optional    

ISO-8601 lower bound for occurred_at. Example: 2026-04-01T00:00:00Z

to   string  optional    

ISO-8601 upper bound for occurred_at. Example: 2026-04-30T23:59:59Z

ip_address   string  optional    

Filter by client IP. Example: 203.0.113.42

Returns a single audit log row by id. * @group Admin API

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/audit-logs/019f0593-c66e-7141-a832-edbf690fced6" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/audit-logs/019f0593-c66e-7141-a832-edbf690fced6"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/audit-logs/019f0593-c66e-7141-a832-edbf690fced6';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/audit-logs/019f0593-c66e-7141-a832-edbf690fced6');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "id": "019fd2b7-f165-7231-8be6-f3c0c9d9acd0",
        "event": "model.created",
        "actor_type": null,
        "actor_id": null,
        "actor_guard": null,
        "subject_type": "App\\Domain\\Employee\\Models\\EmployeeProfile",
        "subject_id": "019fd2b7-f161-73ae-875c-d74efc63d8bb",
        "ip_address": "127.0.0.1",
        "user_agent": "Symfony",
        "session_token_id": null,
        "metadata": {
            "old_values": null,
            "new_values": {
                "user_id": "019fd2b7-f153-7131-a03e-8f1096bf7eeb",
                "status": "active",
                "date_of_birth": "1*****************0",
                "nationality": "AT",
                "address_line_1": "***REDACTED***",
                "address_line_2": "***REDACTED***",
                "postal_code": "1**0",
                "city": "Wien",
                "country": "AT",
                "svs_number": "***MASKED***",
                "tax_id": "e******************************************************************************************************************************************************************************************************9",
                "emergency_contact_name": "M***************",
                "emergency_contact_phone": "********5678",
                "emergency_contact_relationship": "Ehefrau",
                "approved_at": "2026-02-05 16:18:18",
                "id": "019fd2b7-f161-73ae-875c-d74efc63d8bb",
                "updated_at": "2026-08-05 16:18:18",
                "created_at": "2026-08-05 16:18:18"
            },
            "correlation_id": null
        },
        "occurred_at": "2026-08-05T16:18:17.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "6ad4d966-cec8-4745-a221-d1cb548a92f5",
        "timestamp": "2026-08-05T16:18:42.503197Z"
    }
}
 

Request      

GET api/v1/admin/audit-logs/{auditLog_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

auditLog_id   string     

The ID of the auditLog. Example: 019f0593-c66e-7141-a832-edbf690fced6

GDPR

Admin-triggered GDPR Art. 17 (Right to Erasure) anonymization.

requires authentication

Overwrites all PII on the user with non-identifying placeholders, deactivates the account, and revokes all active Sanctum tokens. A mandatory reason is required and is recorded in the audit log.

This operation is irreversible. Foreign-key relationships (donations,

favourites) are preserved for legal and accounting purposes. * @group Admin API

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/anonymize" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/anonymize"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/anonymize';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/anonymize');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "User anonymised successfully.",
    "data": {
        "id": "019fd2b7-f271-73fd-8268-848be381e136",
        "first_name": "Anonymized",
        "last_name": "User",
        "email": "anonymized-019fd2b7-f271-73fd-8268-848be381e136@flexxr.invalid",
        "country_code": null,
        "phone_number": null,
        "status": "deactivated",
        "avatar_url": null,
        "bio": null,
        "date_of_birth": null,
        "country": null,
        "city": null,
        "notify_push": true,
        "notify_email": true,
        "notify_sms": true,
        "notify_marketing": false,
        "email_verified_at": "2026-08-05T16:18:32+00:00",
        "phone_verified_at": "2026-08-05T16:18:32+00:00",
        "created_at": "2026-08-05T16:18:18+00:00",
        "updated_at": "2026-08-05T16:18:41+00:00",
        "employee_profile": null
    },
    "errors": null,
    "meta": {
        "request_id": "942e6a6d-9d0c-4a4f-bc5e-e1de244412e6",
        "timestamp": "2026-08-05T16:18:41.761914Z"
    }
}
 

Request      

POST api/v1/admin/users/{user_id}/anonymize

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Admin-triggered GDPR Art. 15 (Right of Access) data export.

requires authentication

Compiles everything the platform holds about a user into a single JSON envelope. The frontend handles the JSON → file download step

client-side; no streaming is required for v1. * @group Admin API

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/export" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/export"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/export');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "User data exported successfully.",
    "data": {
        "export_metadata": {
            "schema_version": "2.1",
            "export_type": "admin_assisted",
            "exported_at": "2026-08-05T16:18:41.777945Z",
            "exported_by_admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
            "data_subject_id": "019fd2b7-f271-73fd-8268-848be381e136",
            "controller": {
                "name": "Flexxer",
                "address": "Musterstraße 1, 1010 Wien, Österreich",
                "fn": "FN 123456x",
                "uid": "ATU12345678",
                "privacy_contact": "privacy@flexxr.at",
                "support_contact": "support@flexxr.at"
            },
            "legal_basis": {
                "access": "GDPR Art. 15 (Right of access)",
                "portability": "GDPR Art. 20 (Right to data portability)"
            },
            "rights_summary": {
                "access": "Art. 15 — request a copy of your data (this export)",
                "rectification": "Art. 16 — request correction of inaccurate data",
                "erasure": "Art. 17 — request deletion / anonymisation of your account",
                "portability": "Art. 20 — receive your data in a machine-readable format",
                "objection": "Art. 21 — object to processing based on legitimate interests"
            },
            "retention_periods": {
                "account_data": "3 years after last activity",
                "audit_logs": "7 years",
                "donation_receipts": "7 years (fiscal obligation)",
                "session_tokens": "90 days from last use"
            }
        },
        "profile": {
            "id": "019fd2b7-f271-73fd-8268-848be381e136",
            "email": "pending@demo.flexxr.at",
            "first_name": "Anna",
            "last_name": "Neuling",
            "phone_number": "6769876543",
            "email_verified_at": "2026-08-05T16:18:32.000000Z",
            "created_at": "2026-08-05T16:18:18.000000Z"
        },
        "consents": {
            "notify_push": true,
            "notify_email": true,
            "notify_sms": true,
            "notify_marketing": false
        },
        "audit_trail": [],
        "notifications": [],
        "sessions": [
            {
                "name": "docs",
                "created_at": "2026-08-05 16:18:33"
            }
        ],
        "social_accounts": [],
        "two_factor": {
            "method": "totp",
            "is_enabled": true,
            "confirmed_at": "2026-08-05T16:18:32.000000Z",
            "recovery_codes_remaining": 2
        },
        "donations": [],
        "recurring_donations": [],
        "donation_receipts": [],
        "favourites": [],
        "contact_messages": [],
        "support_tickets": [],
        "legal_acceptances": [],
        "exported_at": "2026-08-05T16:18:41.778035Z",
        "exported_by_admin_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "exported_by_self": false
    },
    "errors": null,
    "meta": {
        "request_id": "5fa38b28-b605-4d05-80c8-464f916d5eb5",
        "timestamp": "2026-08-05T16:18:41.783098Z"
    }
}
 

Request      

GET api/v1/admin/users/{user_id}/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Restrict User Processing

requires authentication

Flags the user's data as processing-restricted under GDPR Art. 18.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/users/{user_id}/restrict

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

user   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Lift User Processing Restriction

requires authentication

Clears the processing-restricted flag on the user.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/restrict');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/admin/users/{user_id}/restrict

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

user   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

List Org GDPR Requests

requires authentication

Paginated queue of GDPR requests forwarded by organisations on behalf of a donor/data subject, optionally filtered by status, request type, or organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests?status=pending&type=access&organization_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"rejected\",
    \"type\": \"restriction\",
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests"
);

const params = {
    "status": "pending",
    "type": "access",
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "rejected",
    "type": "restriction",
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'pending',
            'type' => 'access',
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'per_page' => '20',
        ],
        'json' => [
            'status' => 'rejected',
            'type' => 'restriction',
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests')
      .replace(queryParameters: {
        'status': 'pending',
        'type': 'access',
        'organization_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'per_page': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "rejected",
    "type": "restriction",
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "07ec46f6-66c0-43dd-ae6d-08ad888de3cb",
        "timestamp": "2026-08-21T05:14:35.638439Z"
    }
}
 

Request      

GET api/v1/admin/gdpr/org-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by status: pending|in_progress|resolved|rejected. Example: pending

type   string  optional    

Filter by request type: access|erasure|restriction|rectification. Example: access

organization_id   string  optional    

Filter by organisation. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

per_page   integer  optional    

Items per page (max 50). Example: 20

Body Parameters

status   string  optional    

Example: rejected

Must be one of:
  • pending
  • in_progress
  • resolved
  • rejected
type   string  optional    

Example: restriction

Must be one of:
  • access
  • erasure
  • restriction
  • rectification
organization_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Update Org GDPR Request Status

requires authentication

Move an org-forwarded GDPR request into progress, resolve it, or reject it with an optional platform note.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests/architecto/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"resolved\",
    \"platform_note\": \"Deletion completed and confirmed to the organisation.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests/architecto/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "resolved",
    "platform_note": "Deletion completed and confirmed to the organisation."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests/architecto/status';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'resolved',
            'platform_note' => 'Deletion completed and confirmed to the organisation.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/gdpr/org-requests/architecto/status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "resolved",
    "platform_note": "Deletion completed and confirmed to the organisation."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (422, Invalid status):


{
    "status": "VALIDATION_ERROR",
    "message": "The status field is invalid."
}
 

Request      

POST api/v1/admin/gdpr/org-requests/{orgGdprRequest_id}/status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

orgGdprRequest_id   string     

The ID of the orgGdprRequest. Example: architecto

orgGdprRequest   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

status   string     

One of in_progress, resolved, rejected. Example: resolved

platform_note   string  optional    

Example: Deletion completed and confirmed to the organisation.

List Worker GDPR Requests

requires authentication

Paginated queue of GDPR rights requests raised directly by workers, optionally filtered by status, request type, or user. Each row includes sla_days_remaining against the Art. 12(3) one-month response deadline.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests?status=pending&type=restriction&user_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"in_progress\",
    \"type\": \"rectification\",
    \"user_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests"
);

const params = {
    "status": "pending",
    "type": "restriction",
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "in_progress",
    "type": "rectification",
    "user_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'pending',
            'type' => 'restriction',
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'per_page' => '20',
        ],
        'json' => [
            'status' => 'in_progress',
            'type' => 'rectification',
            'user_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests')
      .replace(queryParameters: {
        'status': 'pending',
        'type': 'restriction',
        'user_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'per_page': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "in_progress",
    "type": "rectification",
    "user_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "50b4baf4-7a3c-40a6-9fe6-009be5467bba",
        "timestamp": "2026-08-21T05:14:35.661280Z"
    }
}
 

Request      

GET api/v1/admin/gdpr/user-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by status: pending|in_progress|resolved|rejected. Example: pending

type   string  optional    

Filter by request type: rectification|restriction|objection|other. Example: restriction

user_id   string  optional    

Filter by worker. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

per_page   integer  optional    

Items per page (max 50). Example: 20

Body Parameters

status   string  optional    

Example: in_progress

Must be one of:
  • pending
  • in_progress
  • resolved
  • rejected
type   string  optional    

Example: rectification

Must be one of:
  • rectification
  • restriction
  • objection
  • other
user_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Update Worker GDPR Request Status

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"resolved\",
    \"platform_note\": \"Adresse wurde korrigiert.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "resolved",
    "platform_note": "Adresse wurde korrigiert."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9/status';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'resolved',
            'platform_note' => 'Adresse wurde korrigiert.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/gdpr/user-requests/019fe6e4-bbcd-71fd-918b-effe04df4ff9/status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "resolved",
    "platform_note": "Adresse wurde korrigiert."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (422, Invalid status):


{
    "status": "VALIDATION_ERROR",
    "message": "The status field is invalid."
}
 

Request      

POST api/v1/admin/gdpr/user-requests/{userGdprRequest_id}/status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

userGdprRequest_id   string     

The ID of the userGdprRequest. Example: 019fe6e4-bbcd-71fd-918b-effe04df4ff9

userGdprRequest   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

status   string     

One of in_progress, resolved, rejected. Example: resolved

platform_note   string  optional    

Example: Adresse wurde korrigiert.

List GDPR Deletion Requests

requires authentication

Users with a scheduled data-retention horizon, ordered by how soon it falls due.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/gdpr/deletion-requests?per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/deletion-requests"
);

const params = {
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/gdpr/deletion-requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/gdpr/deletion-requests')
      .replace(queryParameters: {
        'per_page': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "fce2f915-1e78-4278-97ce-28b10af4dac9",
        "timestamp": "2026-08-21T05:14:35.679123Z"
    }
}
 

Request      

GET api/v1/admin/gdpr/deletion-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Items per page (max 50). Example: 20

List GDPR Data Exports

requires authentication

Paginated queue of asynchronous GDPR data-export requests, optionally filtered by status or user.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/gdpr/exports?status=ready&user_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"expired\",
    \"user_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/gdpr/exports"
);

const params = {
    "status": "ready",
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "expired",
    "user_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/gdpr/exports';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'ready',
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'per_page' => '20',
        ],
        'json' => [
            'status' => 'expired',
            'user_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/gdpr/exports')
      .replace(queryParameters: {
        'status': 'ready',
        'user_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'per_page': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "expired",
    "user_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "1670e81f-0fee-45e8-9a49-ae2afa5ee569",
        "timestamp": "2026-08-21T05:14:35.689876Z"
    }
}
 

Request      

GET api/v1/admin/gdpr/exports

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by status: pending|processing|ready|failed|expired. Example: ready

user_id   string  optional    

Filter by user. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

per_page   integer  optional    

Items per page (max 50). Example: 20

Body Parameters

status   string  optional    

Example: expired

Must be one of:
  • pending
  • processing
  • ready
  • failed
  • expired
user_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Auth

Change Admin Password

requires authentication

Allows an authenticated, 2FA-verified admin to change their own password.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/change-password" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"current_password\": \"S3cure-Passw0rd!\",
    \"new_password\": \"S3cure-Passw0rd!\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/change-password"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "current_password": "S3cure-Passw0rd!",
    "new_password": "S3cure-Passw0rd!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/change-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'current_password' => 'S3cure-Passw0rd!',
            'new_password' => 'S3cure-Passw0rd!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/change-password');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "current_password": "S3cure-Passw0rd!",
    "new_password": "S3cure-Passw0rd!"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password updated.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "d2eb3161-d050-4812-80a5-90564e5b4161",
        "timestamp": "2026-08-05T16:18:41.650210Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "current_password",
            "message": "The password is incorrect."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Password Reuse):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "You cannot reuse your last 3 passwords."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/change-password

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

current_password   string     

Example: S3cure-Passw0rd!

new_password   string     

Example: S3cure-Passw0rd!

Get Current Admin

requires authentication

Returns the authenticated admin's profile alongside their resolved ACL manifest (scope, presets, flat permission list, conditional markers).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/auth/me" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/me"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/me');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "profile": {
            "id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
            "name": "Super Admin",
            "email": "office@flexxr.at",
            "role": "super_admin",
            "is_active": true
        },
        "acl": {
            "scope": "admin",
            "presets": [
                "super-admin"
            ],
            "permissions": [
                "admin.admins.create",
                "admin.admins.deactivate",
                "admin.admins.reactivate",
                "admin.admins.reset-2fa",
                "admin.admins.update",
                "admin.admins.update-presets",
                "admin.admins.update-role",
                "admin.admins.view",
                "admin.app-settings.reveal-secret",
                "admin.app-settings.update",
                "admin.app-settings.view",
                "admin.assignments.manage",
                "admin.assignments.view",
                "admin.audit.view",
                "admin.billing.update",
                "admin.billing.view",
                "admin.compliance.aueg-monitor",
                "admin.compliance.minimum-wage-warnings",
                "admin.compliance.view",
                "admin.compliance.working-time-warnings",
                "admin.contracts.manage",
                "admin.contracts.view",
                "admin.employees.approve",
                "admin.employees.flag-high-risk",
                "admin.employees.reactivate",
                "admin.employees.reject",
                "admin.employees.request-correction",
                "admin.employees.review-documents",
                "admin.employees.suspend",
                "admin.employees.validate-svs",
                "admin.employees.view",
                "admin.finance.manage-dunning",
                "admin.finance.manage-invoices",
                "admin.finance.view-invoices",
                "admin.finance.view-payments",
                "admin.gdpr.anonymize",
                "admin.gdpr.export",
                "admin.gdpr.restrict",
                "admin.jobs.approve",
                "admin.jobs.emergency-close",
                "admin.jobs.emergency-reassign",
                "admin.jobs.manage-categories",
                "admin.jobs.manage-document-types",
                "admin.jobs.reject",
                "admin.jobs.view",
                "admin.legal.manage",
                "admin.legal.view",
                "admin.organizations.reactivate",
                "admin.organizations.reject",
                "admin.organizations.reset-password",
                "admin.organizations.review-documents",
                "admin.organizations.suspend",
                "admin.organizations.update",
                "admin.organizations.verify",
                "admin.organizations.view",
                "admin.payroll.download-payslips",
                "admin.payroll.sync",
                "admin.payroll.trigger",
                "admin.payroll.view",
                "admin.ratings.moderate",
                "admin.ratings.view",
                "admin.reports.export",
                "admin.reports.view",
                "admin.security.detect-duplicates",
                "admin.security.manual-review",
                "admin.support.assign-tickets",
                "admin.support.escalate-tickets",
                "admin.support.resolve-tickets",
                "admin.support.update",
                "admin.support.view",
                "admin.support.view-tickets",
                "admin.system.view-health",
                "admin.system.view-metrics",
                "admin.users.change-status",
                "admin.users.reset-password",
                "admin.users.update",
                "admin.users.update-by-admin",
                "admin.users.view"
            ],
            "conditional": {},
            "computed_at": "2026-08-05T16:18:41+00:00"
        },
        "two_factor": {
            "confirmed": true,
            "required": false,
            "setup_required": false
        }
    },
    "errors": null,
    "meta": {
        "request_id": "bbcf7f74-310a-47c7-9394-09d385e741df",
        "timestamp": "2026-08-05T16:18:41.662797Z"
    }
}
 

Request      

GET api/v1/admin/auth/me

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Admin Two-Factor State

requires authentication

Returns the current 2FA configuration for the authenticated admin.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, 2FA Disabled):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "enabled": false,
        "method": null,
        "confirmed_at": null,
        "recovery_codes_remaining": 0
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-10T12:00:00.000000Z"
    }
}
 

Request      

GET api/v1/admin/auth/two-factor

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

User Sessions

List a user's active Sanctum sessions (personal access tokens).

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Sessions retrieved successfully.",
    "data": [
        {
            "id": "4",
            "name": "docs",
            "abilities": [
                "*"
            ],
            "last_used_at": null,
            "expires_at": null,
            "created_at": "2026-08-05T16:18:33.000000Z",
            "is_expired": false
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "0c9f5eef-9166-4173-9389-17d6cc662596",
        "timestamp": "2026-08-05T16:18:41.792412Z"
    }
}
 

Request      

GET api/v1/admin/users/{user_id}/sessions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Revoke ALL Sanctum sessions for a user. Used by admins responding to suspected account compromise or following a privacy support request.

requires authentication

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "All sessions revoked.",
    "data": {
        "revoked_count": 1
    },
    "errors": null,
    "meta": {
        "request_id": "0ab9f276-3175-4f5e-88f2-017b0affb509",
        "timestamp": "2026-08-05T16:18:41.808950Z"
    }
}
 

Request      

DELETE api/v1/admin/users/{user_id}/sessions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

Revoke a single Sanctum session (personal access token) belonging to a user.

requires authentication

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions/architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions/architecto"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/users/019f0593-c75f-7265-bb3d-278a77f5a95f/sessions/architecto');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Session revoked.",
    "data": {
        "id": "4"
    },
    "errors": null,
    "meta": {
        "request_id": "a50d1a40-7b5b-4de9-865c-d54b38b6fc2e",
        "timestamp": "2026-08-05T16:18:41.826845Z"
    }
}
 

Request      

DELETE api/v1/admin/users/{user_id}/sessions/{tokenId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

user_id   string     

The ID of the user. Example: 019f0593-c75f-7265-bb3d-278a77f5a95f

tokenId   string     

Example: architecto

Employee Approval

Suspend an ACTIVE employee.

requires authentication

Suspend Employee

Suspension immediately cancels all future scheduled shifts so the suspended worker can neither appear on rosters nor accrue wages while restricted. The employee keeps login access but sees a restriction notice; the generic reason category is shared, investigation details are not.

After the transaction commits, each affected organisation receives a single ShiftCancelled notification (one per org, deduplicated). A notification failure never aborts the suspension.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/suspend" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"compliance_violation\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/suspend"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "compliance_violation"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/suspend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'compliance_violation',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/suspend');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "compliance_violation"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Employee suspended.",
    "data": {
        "employee_id": "019fd2b7-f279-7142-a68d-0d4563a06070",
        "status": "suspended",
        "cancelled_shifts": 1
    }
}
 

Example response (404, Not Found):


{
    "status": "ERROR",
    "message": "Employee not found."
}
 

Example response (422, Not Active):


{
    "status": "ERROR",
    "message": "Only active employees can be suspended."
}
 

Request      

POST api/v1/admin/employees/{profileId}/suspend

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Example: architecto

Body Parameters

reason   string     

Generic reason category shared with the employee. Example: compliance_violation

Reactivate a SUSPENDED employee.

requires authentication

Reactivate Employee

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/reactivate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"note\": \"Investigation closed, no violation found.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/reactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Investigation closed, no violation found."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/reactivate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'note' => 'Investigation closed, no violation found.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/reactivate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "note": "Investigation closed, no violation found."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Employee reactivated.",
    "data": {
        "employee_id": "019fd2b8-29d8-7088-aaea-ffda7ed96710",
        "status": "active"
    }
}
 

Example response (404, Not Found):


{
    "status": "ERROR",
    "message": "Employee not found."
}
 

Example response (422, Not Suspended):


{
    "status": "ERROR",
    "message": "Only suspended employees can be reactivated."
}
 

Request      

POST api/v1/admin/employees/{profileId}/reactivate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Example: architecto

Body Parameters

note   string  optional    

optional Internal note recorded in the audit trail. Example: Investigation closed, no violation found.

Employee Documents

Resolve a document discrepancy after admin review.

requires authentication

Discrepancies are advisory flags created by OCR when extracted data doesn't match the profile (name, DOB, IBAN). Admins can resolve them after verifying the document is legitimate.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/discrepancies/architecto/resolve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/discrepancies/architecto/resolve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/discrepancies/architecto/resolve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/discrepancies/architecto/resolve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Discrepancy resolved.",
    "data": {
        "discrepancy_id": "uuid",
        "resolved_at": "2026-06-20 12:00"
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Discrepancy not found."
}
 

Example response (422, Already Resolved):


{
    "status": "INVALID_OPERATION",
    "message": "Discrepancy already resolved."
}
 

Request      

POST api/v1/admin/employees/discrepancies/{discrepancyId}/resolve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

discrepancyId   string     

Example: architecto

No-Shows

List No-Shows

requires authentication

Platform-wide list of no-show incidents, with reliability/fraud relevant summary counts computed across every filtered row, not just the current page.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/no-shows?organization_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&date_from=2026-01-01&date_to=2026-01-31&status=detected&is_excused=" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"date_from\": \"2026-08-21T05:14:35\",
    \"date_to\": \"2026-08-21T05:14:35\",
    \"status\": \"excused\",
    \"is_excused\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/no-shows"
);

const params = {
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "date_from": "2026-01-01",
    "date_to": "2026-01-31",
    "status": "detected",
    "is_excused": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "date_from": "2026-08-21T05:14:35",
    "date_to": "2026-08-21T05:14:35",
    "status": "excused",
    "is_excused": false
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/no-shows';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'date_from' => '2026-01-01',
            'date_to' => '2026-01-31',
            'status' => 'detected',
            'is_excused' => '0',
        ],
        'json' => [
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
            'date_from' => '2026-08-21T05:14:35',
            'date_to' => '2026-08-21T05:14:35',
            'status' => 'excused',
            'is_excused' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/no-shows')
      .replace(queryParameters: {
        'organization_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'date_from': '2026-01-01',
        'date_to': '2026-01-31',
        'status': 'detected',
        'is_excused': '',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "date_from": "2026-08-21T05:14:35",
    "date_to": "2026-08-21T05:14:35",
    "status": "excused",
    "is_excused": false
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "4dd51c7f-725f-4721-9e34-00899c8d6bd6",
        "timestamp": "2026-08-21T05:14:35.839287Z"
    }
}
 

Request      

GET api/v1/admin/no-shows

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

organization_id   string  optional    

Filter by organisation. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

date_from   string  optional    

Filter by start date (YYYY-MM-DD). Example: 2026-01-01

date_to   string  optional    

Filter by end date (YYYY-MM-DD). Example: 2026-01-31

status   string  optional    

Filter by status. Example: detected

is_excused   boolean  optional    

Filter by excused status. Example: false

Body Parameters

organization_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

date_from   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:35

date_to   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:35

status   string  optional    

Example: excused

Must be one of:
  • pending
  • detected
  • confirmed
  • replacement_requested
  • replacement_assigned
  • resolved
  • excused
  • disputed
is_excused   boolean  optional    

Example: false

Excuse No-Show

requires authentication

Marks a no-show incident as excused with an optional note. Refused when the incident is already excused or is past the stage where an excuse still applies.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/no-shows/019f34c2-b7a3-7144-96fa-ba76afffcf1e/excuse" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"note\": \"Employee provided a medical certificate.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/no-shows/019f34c2-b7a3-7144-96fa-ba76afffcf1e/excuse"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Employee provided a medical certificate."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/no-shows/019f34c2-b7a3-7144-96fa-ba76afffcf1e/excuse';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'note' => 'Employee provided a medical certificate.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/no-shows/019f34c2-b7a3-7144-96fa-ba76afffcf1e/excuse');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "note": "Employee provided a medical certificate."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (409, Cannot be excused):


{
    "status": "CONFLICT",
    "message": "This incident can no longer be excused."
}
 

Request      

POST api/v1/admin/no-shows/{incident_id}/excuse

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

incident_id   string     

The ID of the incident. Example: 019f34c2-b7a3-7144-96fa-ba76afffcf1e

incident   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

note   string  optional    

Example: Employee provided a medical certificate.

Badges

List Badges

requires authentication

The full badge catalog (including hidden/inactive), with the number of workers who have earned each badge.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/badges" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/badges"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/badges';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/badges');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "5b167a2f-b8ad-43a0-8c3c-562d7aaf84e8",
        "timestamp": "2026-08-21T05:14:35.857334Z"
    }
}
 

Request      

GET api/v1/admin/badges

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Badge

requires authentication

Adds a new badge to the catalog.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/badges" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"slug\": \"perfect-month\",
    \"name\": \"Perfekter Monat\",
    \"description\": \"Keine Ausfälle einen Monat lang.\",
    \"icon\": \"mdi-star\",
    \"color\": \"gold\",
    \"category\": \"reliability\",
    \"tier\": 2,
    \"criteria_type\": \"shifts_completed\",
    \"criteria_threshold\": 10,
    \"points\": 50,
    \"is_active\": true,
    \"is_hidden\": false,
    \"sort_order\": 10
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/badges"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "slug": "perfect-month",
    "name": "Perfekter Monat",
    "description": "Keine Ausfälle einen Monat lang.",
    "icon": "mdi-star",
    "color": "gold",
    "category": "reliability",
    "tier": 2,
    "criteria_type": "shifts_completed",
    "criteria_threshold": 10,
    "points": 50,
    "is_active": true,
    "is_hidden": false,
    "sort_order": 10
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/badges';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'slug' => 'perfect-month',
            'name' => 'Perfekter Monat',
            'description' => 'Keine Ausfälle einen Monat lang.',
            'icon' => 'mdi-star',
            'color' => 'gold',
            'category' => 'reliability',
            'tier' => 2,
            'criteria_type' => 'shifts_completed',
            'criteria_threshold' => 10,
            'points' => 50,
            'is_active' => true,
            'is_hidden' => false,
            'sort_order' => 10,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/badges');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "slug": "perfect-month",
    "name": "Perfekter Monat",
    "description": "Keine Ausfälle einen Monat lang.",
    "icon": "mdi-star",
    "color": "gold",
    "category": "reliability",
    "tier": 2,
    "criteria_type": "shifts_completed",
    "criteria_threshold": 10,
    "points": 50,
    "is_active": true,
    "is_hidden": false,
    "sort_order": 10
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/badges

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

slug   string     

Unique catalog key. Example: perfect-month

name   string     

Example: Perfekter Monat

description   string  optional    

Example: Keine Ausfälle einen Monat lang.

icon   string  optional    

Example: mdi-star

color   string  optional    

Example: gold

category   string     

One of performance, reliability, milestone, special. Example: reliability

tier   integer     

1=bronze, 2=silver, 3=gold. Example: 2

criteria_type   string  optional    

One of shifts_completed, rating_average, reliability_score, consecutive_shifts, total_hours_worked, on_time_arrivals, five_star_ratings, different_venues, weekend_shifts, holiday_shifts. Example: shifts_completed

criteria_threshold   integer  optional    

Example: 10

points   integer  optional    

Example: 50

is_active   boolean  optional    

Example: true

is_hidden   boolean  optional    

Example: false

sort_order   integer  optional    

Example: 10

Update Badge

requires authentication

Updates a catalog badge's definition.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Perfekter Monat\",
    \"description\": \"Keine Ausfälle einen Monat lang.\",
    \"icon\": \"mdi-star\",
    \"color\": \"gold\",
    \"category\": \"reliability\",
    \"tier\": 2,
    \"criteria_type\": \"shifts_completed\",
    \"criteria_threshold\": 10,
    \"points\": 50,
    \"is_active\": true,
    \"is_hidden\": false,
    \"sort_order\": 10
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Perfekter Monat",
    "description": "Keine Ausfälle einen Monat lang.",
    "icon": "mdi-star",
    "color": "gold",
    "category": "reliability",
    "tier": 2,
    "criteria_type": "shifts_completed",
    "criteria_threshold": 10,
    "points": 50,
    "is_active": true,
    "is_hidden": false,
    "sort_order": 10
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Perfekter Monat',
            'description' => 'Keine Ausfälle einen Monat lang.',
            'icon' => 'mdi-star',
            'color' => 'gold',
            'category' => 'reliability',
            'tier' => 2,
            'criteria_type' => 'shifts_completed',
            'criteria_threshold' => 10,
            'points' => 50,
            'is_active' => true,
            'is_hidden' => false,
            'sort_order' => 10,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Perfekter Monat",
    "description": "Keine Ausfälle einen Monat lang.",
    "icon": "mdi-star",
    "color": "gold",
    "category": "reliability",
    "tier": 2,
    "criteria_type": "shifts_completed",
    "criteria_threshold": 10,
    "points": 50,
    "is_active": true,
    "is_hidden": false,
    "sort_order": 10
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/admin/badges/{badge_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

badge_id   string     

The ID of the badge. Example: 019fe42b-65dc-722e-a4c6-9768028255ff

badge   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

name   string  optional    

Example: Perfekter Monat

description   string  optional    

Example: Keine Ausfälle einen Monat lang.

icon   string  optional    

Example: mdi-star

color   string  optional    

Example: gold

category   string  optional    

One of performance, reliability, milestone, special. Example: reliability

tier   integer  optional    

1=bronze, 2=silver, 3=gold. Example: 2

criteria_type   string  optional    

Example: shifts_completed

criteria_threshold   integer  optional    

Example: 10

points   integer  optional    

Example: 50

is_active   boolean  optional    

Example: true

is_hidden   boolean  optional    

Example: false

sort_order   integer  optional    

Example: 10

Delete Badge

requires authentication

Soft-deletes a catalog badge. Existing awards are unaffected.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/admin/badges/{badge_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

badge_id   string     

The ID of the badge. Example: 019fe42b-65dc-722e-a4c6-9768028255ff

badge   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Award Badge

requires authentication

Manually awards a badge to a worker with a reason.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/award" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"user_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"reason\": \"Herausragende Leistung im Juni.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/award"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "reason": "Herausragende Leistung im Juni."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/award';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'reason' => 'Herausragende Leistung im Juni.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/award');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "reason": "Herausragende Leistung im Juni."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (409, Already awarded):


{
    "status": "DUPLICATE_ENTRY",
    "message": "This worker already has this badge."
}
 

Request      

POST api/v1/admin/badges/{badge_id}/award

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

badge_id   string     

The ID of the badge. Example: 019fe42b-65dc-722e-a4c6-9768028255ff

badge   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

user_id   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

reason   string     

Example: Herausragende Leistung im Juni.

Revoke Badge Award

requires authentication

Revokes a badge from the worker who was awarded it.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/awards/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/awards/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/awards/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/badges/019fe42b-65dc-722e-a4c6-9768028255ff/awards/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (404, Not awarded):


{
    "status": "NOT_FOUND",
    "message": "This worker does not have this badge."
}
 

Request      

DELETE api/v1/admin/badges/{badge_id}/awards/{user}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

badge_id   string     

The ID of the badge. Example: 019fe42b-65dc-722e-a4c6-9768028255ff

user   string     

The worker's user id. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

badge   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Job Categories

List Job Categories

requires authentication

Returns all job categories with optional filtering.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/job-categories?active=1&featured=1&parent_id=null+%28for+root+categories%29&search=gastro" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories"
);

const params = {
    "active": "1",
    "featured": "1",
    "parent_id": "null (for root categories)",
    "search": "gastro",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/job-categories';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'active' => '1',
            'featured' => '1',
            'parent_id' => 'null (for root categories)',
            'search' => 'gastro',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/job-categories')
      .replace(queryParameters: {
        'active': '1',
        'featured': '1',
        'parent_id': 'null (for root categories)',
        'search': 'gastro',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "categories": [
            {
                "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
                "slug": "gastro",
                "name": "Gastronomie",
                "name_localized": "Gastronomy",
                "icon": "utensils",
                "kollektivvertrag": "Gastgewerbe",
                "color": null,
                "is_featured": true,
                "parent_id": null,
                "children": []
            }
        ]
    }
}
 

Request      

GET api/v1/admin/job-categories

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

active   boolean  optional    

Filter by active status. Example: true

featured   boolean  optional    

Filter featured categories. Example: true

parent_id   string  optional    

Filter by parent category. Example: null (for root categories)

search   string  optional    

Search by name or slug. Example: gastro

Create Job Category

requires authentication

Creates a new job category for the platform.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Gastronomie\",
    \"name_en\": \"Gastronomy\",
    \"slug\": \"gastro\",
    \"icon\": \"utensils\",
    \"description\": \"Restaurant and hotel jobs\",
    \"kollektivvertrag\": \"Gastgewerbe KV\",
    \"parent_id\": null,
    \"sort_order\": 10,
    \"is_active\": true,
    \"is_featured\": false,
    \"color\": \"#FF5733\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Gastronomie",
    "name_en": "Gastronomy",
    "slug": "gastro",
    "icon": "utensils",
    "description": "Restaurant and hotel jobs",
    "kollektivvertrag": "Gastgewerbe KV",
    "parent_id": null,
    "sort_order": 10,
    "is_active": true,
    "is_featured": false,
    "color": "#FF5733"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/job-categories';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Gastronomie',
            'name_en' => 'Gastronomy',
            'slug' => 'gastro',
            'icon' => 'utensils',
            'description' => 'Restaurant and hotel jobs',
            'kollektivvertrag' => 'Gastgewerbe KV',
            'parent_id' => null,
            'sort_order' => 10,
            'is_active' => true,
            'is_featured' => false,
            'color' => '#FF5733',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/job-categories');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Gastronomie",
    "name_en": "Gastronomy",
    "slug": "gastro",
    "icon": "utensils",
    "description": "Restaurant and hotel jobs",
    "kollektivvertrag": "Gastgewerbe KV",
    "parent_id": null,
    "sort_order": 10,
    "is_active": true,
    "is_featured": false,
    "color": "#FF5733"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Success):


{
    "status": "SUCCESS",
    "data": {
        "category": {}
    }
}
 

Example response (422, Validation Error):


{
    "status": "ERROR",
    "message": "Validation failed"
}
 

Request      

POST api/v1/admin/job-categories

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Category name (German). Example: Gastronomie

name_en   string  optional    

Category name (English). Example: Gastronomy

slug   string  optional    

URL slug. Auto-generated if not provided. Example: gastro

icon   string  optional    

Icon name. Example: utensils

description   string  optional    

Category description. Example: Restaurant and hotel jobs

kollektivvertrag   string  optional    

KV reference for Austria. Example: Gastgewerbe KV

parent_id   string  optional    

Parent category ID for hierarchical structure.

sort_order   integer  optional    

Display order. Example: 10

is_active   boolean  optional    

Whether category is active. Example: true

is_featured   boolean  optional    

Featured on mobile filter. Example: false

color   string  optional    

Hex color for UI. Example: #FF5733

Update Job Category

requires authentication

Updates an existing job category.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Gastronomie\",
    \"name_en\": \"Gastronomy\",
    \"slug\": \"gastro\",
    \"icon\": \"utensils\",
    \"description\": \"Restaurant and hotel jobs\",
    \"kollektivvertrag\": \"Gastgewerbe KV\",
    \"parent_id\": null,
    \"sort_order\": 10,
    \"is_active\": true,
    \"is_featured\": false,
    \"color\": \"#FF5733\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Gastronomie",
    "name_en": "Gastronomy",
    "slug": "gastro",
    "icon": "utensils",
    "description": "Restaurant and hotel jobs",
    "kollektivvertrag": "Gastgewerbe KV",
    "parent_id": null,
    "sort_order": 10,
    "is_active": true,
    "is_featured": false,
    "color": "#FF5733"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Gastronomie',
            'name_en' => 'Gastronomy',
            'slug' => 'gastro',
            'icon' => 'utensils',
            'description' => 'Restaurant and hotel jobs',
            'kollektivvertrag' => 'Gastgewerbe KV',
            'parent_id' => null,
            'sort_order' => 10,
            'is_active' => true,
            'is_featured' => false,
            'color' => '#FF5733',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Gastronomie",
    "name_en": "Gastronomy",
    "slug": "gastro",
    "icon": "utensils",
    "description": "Restaurant and hotel jobs",
    "kollektivvertrag": "Gastgewerbe KV",
    "parent_id": null,
    "sort_order": 10,
    "is_active": true,
    "is_featured": false,
    "color": "#FF5733"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "category": {
            "id": "019fd2b7-f371-732c-8cdb-6de13be42891",
            "slug": "gastro",
            "name": "Gastronomie",
            "name_localized": "Gastronomy",
            "icon": "utensils",
            "kollektivvertrag": "Gastgewerbe KV",
            "color": "#FF5733",
            "is_featured": false,
            "parent_id": null
        }
    }
}
 

Example response (404, Not Found):


{
    "status": "ERROR",
    "message": "Category not found"
}
 

Example response (422, Validation Error):


{
    "status": "ERROR",
    "message": "Validation failed"
}
 

Request      

PUT api/v1/admin/job-categories/{category}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

category   string     

The category ID. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

name   string  optional    

Category name (German). Example: Gastronomie

name_en   string  optional    

Category name (English). Example: Gastronomy

slug   string  optional    

URL slug. Example: gastro

icon   string  optional    

Icon name. Example: utensils

description   string  optional    

Category description. Example: Restaurant and hotel jobs

kollektivvertrag   string  optional    

KV reference. Example: Gastgewerbe KV

parent_id   string  optional    

Parent category ID.

sort_order   integer  optional    

Display order. Example: 10

is_active   boolean  optional    

Whether category is active. Example: true

is_featured   boolean  optional    

Featured on mobile. Example: false

color   string  optional    

Hex color. Example: #FF5733

Delete Job Category

requires authentication

Soft-deletes a job category. Categories with active jobs cannot be deleted.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000?force=" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000"
);

const params = {
    "force": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'force' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/job-categories/550e8400-e29b-41d4-a716-446655440000')
      .replace(queryParameters: {
        'force': '',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Category deleted"
}
 

Example response (404, Not Found):


{
    "status": "ERROR",
    "message": "Category not found"
}
 

Example response (409, Conflict):


{
    "status": "ERROR",
    "message": "Category has active jobs"
}
 

Request      

DELETE api/v1/admin/job-categories/{category}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

category   string     

The category ID. Example: 550e8400-e29b-41d4-a716-446655440000

Query Parameters

force   boolean  optional    

Force delete even with jobs (moves jobs to uncategorized). Example: false

Job Document Types

List Job Document Types

requires authentication

List every system document type with its company-visibility flag so an admin can choose which the companies may require when authoring a job.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/job-document-types" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/job-document-types"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/job-document-types';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/job-document-types');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "document_types": [
            {
                "id": "d10619da-fd23-4dab-bcd1-73f8b2387be5",
                "code": "passport",
                "label": "Passport",
                "company_selectable": true,
                "sort_order": 10
            },
            {
                "id": "9ee56a86-9282-45b4-b735-f218aae16759",
                "code": "id_card",
                "label": "ID card",
                "company_selectable": true,
                "sort_order": 20
            },
            {
                "id": "f5348f1b-2f15-4c41-a5fe-b2f99c775ee2",
                "code": "driver_license",
                "label": "Driver's licence",
                "company_selectable": true,
                "sort_order": 30
            },
            {
                "id": "7d395af4-f896-46df-bbc4-671ae40f4f8a",
                "code": "work_permit",
                "label": "Work permit",
                "company_selectable": true,
                "sort_order": 40
            },
            {
                "id": "84af4d09-035f-43e6-bd53-2ab18370338a",
                "code": "residence_permit",
                "label": "Residence permit",
                "company_selectable": true,
                "sort_order": 50
            },
            {
                "id": "2f3704b2-e39a-4eeb-8864-56bf52f3ed28",
                "code": "rot_weiss_rot_karte",
                "label": "Rot-Weiß-Rot card",
                "company_selectable": true,
                "sort_order": 60
            },
            {
                "id": "9f171c88-d0da-4d68-ba57-87402c4a0a2c",
                "code": "blue_card",
                "label": "EU Blue Card",
                "company_selectable": true,
                "sort_order": 70
            },
            {
                "id": "bedbfb30-1731-4887-9a3e-b01d77eb0577",
                "code": "bank_statement",
                "label": "Bank statement",
                "company_selectable": true,
                "sort_order": 80
            },
            {
                "id": "45eff414-51e3-4c74-9f12-5b7b600ea715",
                "code": "proof_of_address",
                "label": "Proof of address",
                "company_selectable": true,
                "sort_order": 90
            },
            {
                "id": "7da8605b-3108-41b7-ae37-57b6fba26ef5",
                "code": "social_insurance_card",
                "label": "Social insurance record",
                "company_selectable": true,
                "sort_order": 100
            },
            {
                "id": "004dcced-7dc0-41b5-86dc-ebe2d5098fe2",
                "code": "tax_document",
                "label": "Tax document",
                "company_selectable": true,
                "sort_order": 110
            },
            {
                "id": "88d1719e-fd3f-47f7-bfe9-418db3353b6a",
                "code": "certification",
                "label": "Certification",
                "company_selectable": true,
                "sort_order": 120
            },
            {
                "id": "9c528cb6-5ed6-418d-85ee-98d5087c90cf",
                "code": "health_certificate",
                "label": "Health certificate",
                "company_selectable": true,
                "sort_order": 130
            },
            {
                "id": "04ebe252-d4c3-40e2-b4e4-ee8a9f636d1b",
                "code": "criminal_record_check",
                "label": "Criminal record check",
                "company_selectable": true,
                "sort_order": 140
            },
            {
                "id": "fe6ae84f-7733-49c9-a8de-20672765d2bf",
                "code": "other",
                "label": "Other",
                "company_selectable": true,
                "sort_order": 150
            }
        ]
    }
}
 

Request      

GET api/v1/admin/job-document-types

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Job Document Type

requires authentication

Toggle whether companies may pick a document type (and its ordering). The set of types is fixed by the system enum, so only visibility and order are editable — not the code or creation of new types.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/job-document-types/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"company_selectable\": true,
    \"sort_order\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/job-document-types/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "company_selectable": true,
    "sort_order": 1
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/job-document-types/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'company_selectable' => true,
            'sort_order' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/job-document-types/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "company_selectable": true,
    "sort_order": 1
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/admin/job-document-types/{documentType}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

documentType   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

company_selectable   boolean  optional    

Example: true

sort_order   integer  optional    

Example: 1

Contracts

List Contracts

requires authentication

Paginated, platform-wide list of contracts, filterable by type, status, organisation, worker, or contract number.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/contracts?type=ueberlassungsvertrag&status=signed&organization_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&user_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&contract_number=UEV-2026-000123&per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"ueberlassungsvertrag\",
    \"status\": \"signed\",
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"user_id\": \"6b72fe4a-5b40-307c-bc24-f79acf9a1bb9\",
    \"contract_number\": \"m\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/contracts"
);

const params = {
    "type": "ueberlassungsvertrag",
    "status": "signed",
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "contract_number": "UEV-2026-000123",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "ueberlassungsvertrag",
    "status": "signed",
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "user_id": "6b72fe4a-5b40-307c-bc24-f79acf9a1bb9",
    "contract_number": "m"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/contracts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'type' => 'ueberlassungsvertrag',
            'status' => 'signed',
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'contract_number' => 'UEV-2026-000123',
            'per_page' => '20',
        ],
        'json' => [
            'type' => 'ueberlassungsvertrag',
            'status' => 'signed',
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
            'user_id' => '6b72fe4a-5b40-307c-bc24-f79acf9a1bb9',
            'contract_number' => 'm',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/contracts')
      .replace(queryParameters: {
        'type': 'ueberlassungsvertrag',
        'status': 'signed',
        'organization_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'user_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'contract_number': 'UEV-2026-000123',
        'per_page': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "type": "ueberlassungsvertrag",
    "status": "signed",
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "user_id": "6b72fe4a-5b40-307c-bc24-f79acf9a1bb9",
    "contract_number": "m"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "f605c85b-dbfe-4bd5-a2d9-3e642710bcbd",
        "timestamp": "2026-08-21T05:14:36.264377Z"
    }
}
 

Request      

GET api/v1/admin/contracts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

type   string  optional    

Filter by type: rahmenvertrag|ueberlassungsvertrag|dienstvertrag. Example: ueberlassungsvertrag

status   string  optional    

Filter by status: draft|pending_signature|signed|active|completed|revoked|expired. Example: signed

organization_id   string  optional    

Filter by organisation. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

user_id   string  optional    

Filter by worker. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

contract_number   string  optional    

Search by contract number. Example: UEV-2026-000123

per_page   integer  optional    

Items per page (max 50). Example: 20

Body Parameters

type   string  optional    

Example: ueberlassungsvertrag

Must be one of:
  • rahmenvertrag
  • ueberlassungsvertrag
  • dienstvertrag
status   string  optional    

Example: signed

Must be one of:
  • draft
  • pending_signature
  • signed
  • active
  • completed
  • revoked
  • expired
organization_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

user_id   string  optional    

Must be a valid UUID. Example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9

contract_number   string  optional    

Must not be greater than 50 characters. Example: m

Show Contract

requires authentication

Contract detail including the signed shift assignments it covers and a null-safe download URL.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/contracts/019fdbed-1326-732f-8e9c-68bd4a59d120" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/contracts/019fdbed-1326-732f-8e9c-68bd4a59d120"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/contracts/019fdbed-1326-732f-8e9c-68bd4a59d120';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/contracts/019fdbed-1326-732f-8e9c-68bd4a59d120');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "6b72b314-73fd-45bb-ae86-1dbcd3058f40",
        "timestamp": "2026-08-21T05:14:36.276577Z"
    }
}
 

Request      

GET api/v1/admin/contracts/{contract_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

contract_id   string     

The ID of the contract. Example: 019fdbed-1326-732f-8e9c-68bd4a59d120

contract   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Messaging

List Conversations

requires authentication

Paginated, platform-wide list of conversations, optionally filtered by organisation, user, or a free-text search against the subject.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/messages/conversations?organization_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&user_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&q=Schicht&per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"user_id\": \"6b72fe4a-5b40-307c-bc24-f79acf9a1bb9\",
    \"q\": \"m\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/messages/conversations"
);

const params = {
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "q": "Schicht",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "user_id": "6b72fe4a-5b40-307c-bc24-f79acf9a1bb9",
    "q": "m"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/messages/conversations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'q' => 'Schicht',
            'per_page' => '20',
        ],
        'json' => [
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
            'user_id' => '6b72fe4a-5b40-307c-bc24-f79acf9a1bb9',
            'q' => 'm',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/messages/conversations')
      .replace(queryParameters: {
        'organization_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'user_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'q': 'Schicht',
        'per_page': '20',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "user_id": "6b72fe4a-5b40-307c-bc24-f79acf9a1bb9",
    "q": "m"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "312f42e4-0d24-4b77-b40b-c08cbc8cb1e2",
        "timestamp": "2026-08-21T05:14:36.300932Z"
    }
}
 

Request      

GET api/v1/admin/messages/conversations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

organization_id   string  optional    

Filter by organisation. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

user_id   string  optional    

Filter by employee. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

q   string  optional    

Free-text search against the conversation subject. Example: Schicht

per_page   integer  optional    

Items per page (max 50). Example: 20

Body Parameters

organization_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

user_id   string  optional    

Must be a valid UUID. Example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9

q   string  optional    

Must not be greater than 255 characters. Example: m

Show Conversation Thread

requires authentication

Paginated, read-only message history for a conversation. Sender names are resolved via bulk join queries rather than per-message lookups.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329?per_page=50&before=2026-06-01T00%3A00%3A00Z" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"per_page\": 1,
    \"before\": \"2026-08-21T05:14:36\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329"
);

const params = {
    "per_page": "50",
    "before": "2026-06-01T00:00:00Z",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "per_page": 1,
    "before": "2026-08-21T05:14:36"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '50',
            'before' => '2026-06-01T00:00:00Z',
        ],
        'json' => [
            'per_page' => 1,
            'before' => '2026-08-21T05:14:36',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329')
      .replace(queryParameters: {
        'per_page': '50',
        'before': '2026-06-01T00:00:00Z',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "per_page": 1,
    "before": "2026-08-21T05:14:36"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "db671b12-e2fc-457e-b129-8de92ac9460f",
        "timestamp": "2026-08-21T05:14:36.315491Z"
    }
}
 

Request      

GET api/v1/admin/messages/conversations/{conversation_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

conversation_id   string     

The ID of the conversation. Example: 019fd51d-53c7-73d0-880d-8edee1800329

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Query Parameters

per_page   integer  optional    

Items per page (max 50). Example: 50

before   string  optional    

Only messages sent before this ISO8601 timestamp. Example: 2026-06-01T00:00:00Z

Body Parameters

per_page   integer  optional    

Must be at least 1. Must not be greater than 50. Example: 1

before   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

System

Get System Health

requires authentication

Latest result per health check and the overall derived status (healthy, degraded, or unhealthy).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/system/health" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/system/health"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/system/health';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/system/health');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "d067ae32-0f50-4752-8381-87f0a057109e",
        "timestamp": "2026-08-21T05:14:36.361872Z"
    }
}
 

Request      

GET api/v1/admin/system/health

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Fraud

Show Fraud Case

requires authentication

Full detail of a fraud case: the entities involved, the evidence the signal collected, and its review history.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "bd6c8605-e0fc-4a2c-9735-10a25d924945",
        "timestamp": "2026-08-21T05:14:36.378810Z"
    }
}
 

Request      

GET api/v1/admin/fraud/cases/{case_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

case_id   string     

The ID of the case. Example: 019ffe44-1675-717e-a552-4f410e68ac0f

case   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Update Fraud Case Status

requires authentication

Move a fraud case into review, confirm it, or dismiss it with resolution notes. A dismissed case is left untouched by the nightly fraud:evaluate sweep until a reviewer reopens it.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"confirmed\",
    \"resolution_notes\": \"Confirmed — same person operating two accounts.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "confirmed",
    "resolution_notes": "Confirmed — same person operating two accounts."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f/status';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'confirmed',
            'resolution_notes' => 'Confirmed — same person operating two accounts.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/fraud/cases/019ffe44-1675-717e-a552-4f410e68ac0f/status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "confirmed",
    "resolution_notes": "Confirmed — same person operating two accounts."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (422, Invalid status):


{
    "status": "VALIDATION_ERROR",
    "message": "The status field is invalid."
}
 

Request      

POST api/v1/admin/fraud/cases/{case_id}/status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

case_id   string     

The ID of the case. Example: 019ffe44-1675-717e-a552-4f410e68ac0f

case   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

status   string     

One of reviewing, confirmed, dismissed. Example: confirmed

resolution_notes   string  optional    

Example: Confirmed — same person operating two accounts.

Update Fraud Rule

requires authentication

Enable/disable a fraud signal or update its threshold/window config. The signal key and severity are fixed at the code level and cannot be changed here.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/fraud/rules/019fe183-5843-7311-9ead-2251ec85b3f1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"is_active\": true,
    \"config\": {
        \"min_shared_users\": 3
    }
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/fraud/rules/019fe183-5843-7311-9ead-2251ec85b3f1"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "is_active": true,
    "config": {
        "min_shared_users": 3
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/fraud/rules/019fe183-5843-7311-9ead-2251ec85b3f1';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'is_active' => true,
            'config' => ['min_shared_users' => 3],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/fraud/rules/019fe183-5843-7311-9ead-2251ec85b3f1');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "is_active": true,
    "config": {
        "min_shared_users": 3
    }
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/admin/fraud/rules/{rule_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

rule_id   string     

The ID of the rule. Example: 019fe183-5843-7311-9ead-2251ec85b3f1

rule   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

is_active   boolean  optional    

Example: true

config   object  optional    

Signal-specific thresholds/windows.

Admins

List Admins

requires authentication

Returns a paginated list of all admin accounts, with optional filters.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admins?search=john&role=admin&is_active=1&per_page=15&sort=name&order=asc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins"
);

const params = {
    "search": "john",
    "role": "admin",
    "is_active": "1",
    "per_page": "15",
    "sort": "name",
    "order": "asc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'search' => 'john',
            'role' => 'admin',
            'is_active' => '1',
            'per_page' => '15',
            'sort' => 'name',
            'order' => 'asc',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins')
      .replace(queryParameters: {
        'search': 'john',
        'role': 'admin',
        'is_active': '1',
        'per_page': '15',
        'sort': 'name',
        'order': 'asc',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "864a4b86-aeb4-4ca0-b5dc-29ebed259c97",
        "timestamp": "2026-08-05T16:18:42.604748Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 0
    }
}
 

Example response (403, Forbidden):


{
    "status": "FORBIDDEN",
    "message": "Forbidden.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Forbidden."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

GET api/v1/admin/admins

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

search   string  optional    

Partial match on name or email. Example: john

role   string  optional    

Filter by role (super_admin, admin, moderator). Example: admin

is_active   boolean  optional    

Filter by active status. Example: true

per_page   integer  optional    

Items per page. Example: 15

sort   string  optional    

Column to sort by. Example: name

order   string  optional    

"asc" or "desc". Example: asc

Export Admins

requires authentication

Streams the full admin roster as a CSV file.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/admins/export" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/admins/export"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/admins/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/admins/export');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "faad9a42-9a68-459c-b2a7-61c6d335c075",
        "timestamp": "2026-08-21T05:14:36.475427Z"
    }
}
 

Request      

GET api/v1/admin/admins/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Invoices

List Invoices

requires authentication

List invoices with filtering and pagination.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/invoices" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/invoices"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/invoices';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/invoices');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "invoices": [
            {
                "id": "3257a732-8d8a-4de5-8dee-a54e85c7e111",
                "invoice_number": "RE-2026-055747",
                "organization": {
                    "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                    "name": "Caritas"
                },
                "period": "2026-08",
                "status": "draft",
                "status_label": "Draft",
                "subtotal_cents": 55952,
                "vat_amount_cents": 11190,
                "total_cents": 67142,
                "vat_rate_percent": 20,
                "line_items_count": 0,
                "invoice_date": "2026-08-05",
                "due_date": "2026-08-19",
                "paid_at": null,
                "created_at": "2026-08-05T16:18:32+00:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/admin/invoices

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Show Invoice

requires authentication

Show detailed invoice information for an admin, across any organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/invoices/019f1bd5-82b2-7341-abb8-e8f08fd445c3" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/invoices/019f1bd5-82b2-7341-abb8-e8f08fd445c3"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/invoices/019f1bd5-82b2-7341-abb8-e8f08fd445c3';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/invoices/019f1bd5-82b2-7341-abb8-e8f08fd445c3');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "invoice": {
            "id": "3257a732-8d8a-4de5-8dee-a54e85c7e111",
            "invoice_number": "RE-2026-055747",
            "organization": {
                "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
                "name": "Caritas"
            },
            "period": "2026-08",
            "status": "draft",
            "status_label": "Draft",
            "reference": null,
            "notes": null,
            "summary": {
                "subtotal_cents": 55952,
                "vat_rate_percent": 20,
                "vat_amount_cents": 11190,
                "platform_fee_cents": 0,
                "total_cents": 67142,
                "paid_amount_cents": 0,
                "remaining_cents": 67142
            },
            "dates": {
                "invoice_date": "2026-08-05",
                "due_date": "2026-08-19",
                "sent_at": null,
                "paid_at": null
            },
            "payment_reference": null,
            "pdf_path": null,
            "line_items": [],
            "created_at": "2026-08-05T16:18:32+00:00",
            "updated_at": "2026-08-05T16:18:32+00:00"
        }
    }
}
 

Request      

GET api/v1/admin/invoices/{invoice_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019f1bd5-82b2-7341-abb8-e8f08fd445c3

invoice   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Wallet

GET /admin/wallet/withdrawals — every company's withdrawal requests, newest first, so admins can find the ones awaiting review instead of relying on each company's own history endpoint. Optional `?status=` filter.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "architecto"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "architecto"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "withdrawals": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/admin/wallet/withdrawals

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

status   string  optional    

Example: architecto

POST /admin/wallet/withdrawals/{withdrawal}/approve — approve a pending, over-threshold withdrawal and run the card refund.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/approve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/approve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/approve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/approve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/wallet/withdrawals/{withdrawal}/approve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

withdrawal   string     

The withdrawal. Example: architecto

POST /admin/wallet/withdrawals/{withdrawal}/reject — reject a pending withdrawal and release its reserved funds back to the company.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/wallet/withdrawals/architecto/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/wallet/withdrawals/{withdrawal}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

withdrawal   string     

The withdrawal. Example: architecto

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Assignments

List Assignments

requires authentication

Every assignment across all organisations, for operations oversight — the platform-wide view of who is working where, which the per-company endpoint cannot give because it is scoped to one organisation.

Unfiltered this spans the whole history, newest first, since the rows an operator acts on are the ones nearest to now.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/assignments?status=checked_in&organization_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&date_from=2026-07-01&date_to=2026-07-31&search=Brunner&per_page=25" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"hours_confirmed\",
    \"organization_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"date_from\": \"2026-08-21T05:14:36\",
    \"date_to\": \"2026-08-21T05:14:36\",
    \"search\": \"g\",
    \"per_page\": 16
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/assignments"
);

const params = {
    "status": "checked_in",
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "date_from": "2026-07-01",
    "date_to": "2026-07-31",
    "search": "Brunner",
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "hours_confirmed",
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36",
    "search": "g",
    "per_page": 16
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/assignments';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'checked_in',
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'date_from' => '2026-07-01',
            'date_to' => '2026-07-31',
            'search' => 'Brunner',
            'per_page' => '25',
        ],
        'json' => [
            'status' => 'hours_confirmed',
            'organization_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
            'date_from' => '2026-08-21T05:14:36',
            'date_to' => '2026-08-21T05:14:36',
            'search' => 'g',
            'per_page' => 16,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/assignments')
      .replace(queryParameters: {
        'status': 'checked_in',
        'organization_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'date_from': '2026-07-01',
        'date_to': '2026-07-31',
        'search': 'Brunner',
        'per_page': '25',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "hours_confirmed",
    "organization_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36",
    "search": "g",
    "per_page": 16
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "assignments": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 16,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/admin/assignments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by assignment status. Allowed: awaiting_signature, signed, checked_in, completed, hours_confirmed, no_show, cancelled, disputed, expired. Example: checked_in

organization_id   string  optional    

Restrict to one organisation. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

date_from   string  optional    

Earliest shift date (YYYY-MM-DD). Example: 2026-07-01

date_to   string  optional    

Latest shift date (YYYY-MM-DD). Example: 2026-07-31

search   string  optional    

Match on worker name or job title. Example: Brunner

per_page   integer  optional    

Items per page (max 100). Example: 25

Body Parameters

status   string  optional    

Example: hours_confirmed

Must be one of:
  • awaiting_signature
  • signed
  • checked_in
  • completed
  • hours_confirmed
  • no_show
  • cancelled
  • disputed
  • expired
organization_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

date_from   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

date_to   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

search   string  optional    

Must not be greater than 100 characters. Example: g

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 16

Decide a Shift Request (Admin Override)

requires authentication

Approve or decline a worker's shift request on behalf of the company — used when a company is unresponsive or a decision needs correcting.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/shift-requests/019fdd1b-c901-7051-b2ed-3a96150483ed/decide" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"approve\": true,
    \"note\": \"Overtime was not authorised on site.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/shift-requests/019fdd1b-c901-7051-b2ed-3a96150483ed/decide"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "approve": true,
    "note": "Overtime was not authorised on site."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/shift-requests/019fdd1b-c901-7051-b2ed-3a96150483ed/decide';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'approve' => true,
            'note' => 'Overtime was not authorised on site.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/shift-requests/019fdd1b-c901-7051-b2ed-3a96150483ed/decide');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "approve": true,
    "note": "Overtime was not authorised on site."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (409, Already decided):


{
    "status": "INVALID_OPERATION",
    "message": "This request has already been decided."
}
 

Request      

POST api/v1/admin/shift-requests/{shiftRequest_id}/decide

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftRequest_id   string     

The ID of the shiftRequest. Example: 019fdd1b-c901-7051-b2ed-3a96150483ed

shiftRequest   string     

The request being decided. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

approve   boolean     

Whether the request is granted. Example: true

note   string  optional    

The reason. Required when declining. Example: Overtime was not authorised on site.

Ratings

List Ratings

requires authentication

Ratings exchanged between companies and workers, newest first, for moderation oversight.

Anonymity is a promise made to the rater in the apps, not a rule for this endpoint: moderation is impossible without knowing who wrote what, so the identities are returned here and the obligation moves to who may call it.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/ratings?min_score=2&has_comment=1&per_page=25" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"min_score\": 1,
    \"has_comment\": false,
    \"per_page\": 22
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/ratings"
);

const params = {
    "min_score": "2",
    "has_comment": "1",
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "min_score": 1,
    "has_comment": false,
    "per_page": 22
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/ratings';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'min_score' => '2',
            'has_comment' => '1',
            'per_page' => '25',
        ],
        'json' => [
            'min_score' => 1,
            'has_comment' => false,
            'per_page' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/ratings')
      .replace(queryParameters: {
        'min_score': '2',
        'has_comment': '1',
        'per_page': '25',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "min_score": 1,
    "has_comment": false,
    "per_page": 22
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "ratings": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 22,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/admin/ratings

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

min_score   integer  optional    

Only ratings at or below this overall score, for finding complaints. Example: 2

has_comment   boolean  optional    

Only ratings carrying free text. Example: true

per_page   integer  optional    

Items per page (max 100). Example: 25

Body Parameters

min_score   integer  optional    

Must be at least 1. Must not be greater than 5. Example: 1

has_comment   boolean  optional    

Example: false

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 22

Compliance

List Compliance Violations

requires authentication

List compliance violations with filtering.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/compliance/violations" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/compliance/violations"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/compliance/violations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/compliance/violations');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "violations": [],
        "summary": {
            "total_unresolved": 0,
            "critical_unresolved": 0
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/admin/compliance/violations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Resolve Compliance Violation

requires authentication

Resolve a compliance violation.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/compliance/violations/architecto/resolve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"notes\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/compliance/violations/architecto/resolve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "notes": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/compliance/violations/architecto/resolve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'notes' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/compliance/violations/architecto/resolve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "notes": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/compliance/violations/{violation_id}/resolve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

violation_id   string     

The ID of the violation. Example: architecto

violation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

notes   string  optional    

Example: Beispieltext

Generate Compliance Report

requires authentication

Generate compliance report for a period.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/compliance/reports" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"organization_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"from\": \"2026-09-30\",
    \"to\": \"2026-09-30\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/compliance/reports"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "from": "2026-09-30",
    "to": "2026-09-30"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/compliance/reports';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'organization_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'from' => '2026-09-30',
            'to' => '2026-09-30',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/compliance/reports');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "organization_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "from": "2026-09-30",
    "to": "2026-09-30"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "report": {
            "period": {
                "from": "2026-09-30",
                "to": "2026-09-30"
            },
            "total_violations": 0,
            "resolved": 0,
            "unresolved": 0,
            "by_severity": [],
            "by_type": [],
            "compliance_score": 100
        }
    }
}
 

Request      

POST api/v1/admin/compliance/reports

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

organization_id   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

from   date     

Example: 2026-09-30

to   date     

Example: 2026-09-30

List Fallweise Flags

requires authentication

List fallweise-Beschäftigung Ampel flags (yellow/red engagements) with filtering — the audit trail proving casual employment is monitored for regularity (Beschäftigungskonzept §3).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/compliance/fallweise-flags" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/compliance/fallweise-flags"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/compliance/fallweise-flags';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/compliance/fallweise-flags');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "flags": [],
        "summary": {
            "yellow": 0,
            "red": 0
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/admin/compliance/fallweise-flags

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Reports

Get Reports Summary

requires authentication

Platform-wide KPI groups — assignments, hours, money, growth, compliance, ratings — for a date range. Defaults to the current calendar month.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/reports/summary?date_from=2026-05-01&date_to=2026-05-31" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"date_from\": \"2026-08-21T05:14:36\",
    \"date_to\": \"2026-08-21T05:14:36\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/reports/summary"
);

const params = {
    "date_from": "2026-05-01",
    "date_to": "2026-05-31",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/reports/summary';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'date_from' => '2026-05-01',
            'date_to' => '2026-05-31',
        ],
        'json' => [
            'date_from' => '2026-08-21T05:14:36',
            'date_to' => '2026-08-21T05:14:36',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/reports/summary')
      .replace(queryParameters: {
        'date_from': '2026-05-01',
        'date_to': '2026-05-31',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "2ef084b4-a17b-462a-8e18-7786504d90af",
        "timestamp": "2026-08-21T05:14:36.739104Z"
    }
}
 

Request      

GET api/v1/admin/reports/summary

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

date_from   string  optional    

optional Start of the report period (YYYY-MM-DD). Defaults to first day of current month. Example: 2026-05-01

date_to   string  optional    

optional End of the report period (YYYY-MM-DD). Defaults to last day of current month. Example: 2026-05-31

Body Parameters

date_from   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

date_to   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

Get Reports Series

requires authentication

Weekly time-series of completed assignments, top-up volume, new workers, and compliance violations for a date range. Defaults to the current calendar month.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/reports/series?date_from=2026-05-01&date_to=2026-05-31" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"date_from\": \"2026-08-21T05:14:36\",
    \"date_to\": \"2026-08-21T05:14:36\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/reports/series"
);

const params = {
    "date_from": "2026-05-01",
    "date_to": "2026-05-31",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/reports/series';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'date_from' => '2026-05-01',
            'date_to' => '2026-05-31',
        ],
        'json' => [
            'date_from' => '2026-08-21T05:14:36',
            'date_to' => '2026-08-21T05:14:36',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/reports/series')
      .replace(queryParameters: {
        'date_from': '2026-05-01',
        'date_to': '2026-05-31',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "9d489aaf-a4e2-4696-a234-1eca78e8cb4f",
        "timestamp": "2026-08-21T05:14:36.753892Z"
    }
}
 

Request      

GET api/v1/admin/reports/series

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

date_from   string  optional    

optional Start of the report period (YYYY-MM-DD). Defaults to first day of current month. Example: 2026-05-01

date_to   string  optional    

optional End of the report period (YYYY-MM-DD). Defaults to last day of current month. Example: 2026-05-31

Body Parameters

date_from   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

date_to   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

Export Reports

requires authentication

Streams one reports section — assignments, money, growth, or compliance — as a CSV download for the given date range.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/reports/export?section=assignments&date_from=2026-05-01&date_to=2026-05-31" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"section\": \"architecto\",
    \"date_from\": \"2026-08-21T05:14:36\",
    \"date_to\": \"2026-08-21T05:14:36\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/reports/export"
);

const params = {
    "section": "assignments",
    "date_from": "2026-05-01",
    "date_to": "2026-05-31",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "section": "architecto",
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/reports/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'section' => 'assignments',
            'date_from' => '2026-05-01',
            'date_to' => '2026-05-31',
        ],
        'json' => [
            'section' => 'architecto',
            'date_from' => '2026-08-21T05:14:36',
            'date_to' => '2026-08-21T05:14:36',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/reports/export')
      .replace(queryParameters: {
        'section': 'assignments',
        'date_from': '2026-05-01',
        'date_to': '2026-05-31',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "section": "architecto",
    "date_from": "2026-08-21T05:14:36",
    "date_to": "2026-08-21T05:14:36"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "9cd044c7-a95b-4c30-994a-cd26f220e034",
        "timestamp": "2026-08-21T05:14:36.768364Z"
    }
}
 

Request      

GET api/v1/admin/reports/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

section   string     

Section to export: assignments, money, growth, or compliance. Example: assignments

date_from   string  optional    

optional Start of the report period (YYYY-MM-DD). Defaults to first day of current month. Example: 2026-05-01

date_to   string  optional    

optional End of the report period (YYYY-MM-DD). Defaults to last day of current month. Example: 2026-05-31

Body Parameters

section   string     

Example: architecto

date_from   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

date_to   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:36

requires authentication

List legal document versions (terms, privacy/Datenschutz, AÜG info, …).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/legal/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/legal/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/legal/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/legal/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": [
        {
            "id": "019fd2b7-ef68-7312-a1a3-6b14820cf9e2",
            "document_type": "privacy_policy",
            "document_label": "Privacy Policy",
            "version": 1,
            "locale": "de",
            "title": "Datenschutzerklärung",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 1,
            "pending_count": 15,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef6c-7272-a1a7-6eebcd113ec1",
            "document_type": "privacy_policy",
            "document_label": "Privacy Policy",
            "version": 1,
            "locale": "en",
            "title": "Privacy Policy",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 0,
            "pending_count": 16,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef6f-72ae-8c86-d9977da26ec0",
            "document_type": "terms_of_service",
            "document_label": "Terms and Conditions",
            "version": 1,
            "locale": "de",
            "title": "Allgemeine Geschäftsbedingungen (AGB)",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 1,
            "pending_count": 15,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef73-7323-9eea-ef440b63f842",
            "document_type": "terms_of_service",
            "document_label": "Terms and Conditions",
            "version": 1,
            "locale": "en",
            "title": "Terms of Service",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 0,
            "pending_count": 16,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef76-73b5-a9c5-d7cca778324b",
            "document_type": "data_processing",
            "document_label": "Data Processing",
            "version": 1,
            "locale": "de",
            "title": "Einwilligung zur Datenverarbeitung",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 1,
            "pending_count": 15,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef79-7064-b74c-069b159b28f9",
            "document_type": "data_processing",
            "document_label": "Data Processing",
            "version": 1,
            "locale": "en",
            "title": "Data Processing Consent",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 0,
            "pending_count": 16,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef7c-732a-b29e-ef789f899c9d",
            "document_type": "aug_info",
            "document_label": "Temporary Employment Contract (AÜG)",
            "version": 1,
            "locale": "de",
            "title": "Information gemäß AÜG (Arbeitskräfteüberlassungsgesetz)",
            "public_url": null,
            "requires_reacceptance": false,
            "is_active": true,
            "requires_consent": false,
            "accepted_count": 0,
            "pending_count": 0,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef7f-72f2-9fb3-4a25c846761e",
            "document_type": "aug_info",
            "document_label": "Temporary Employment Contract (AÜG)",
            "version": 1,
            "locale": "en",
            "title": "Information pursuant to AÜG (Temporary Work Act)",
            "public_url": null,
            "requires_reacceptance": false,
            "is_active": true,
            "requires_consent": false,
            "accepted_count": 0,
            "pending_count": 0,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef83-7298-a78c-af4b1e36b67b",
            "document_type": "svnr_consent",
            "document_label": "Social Security Number Consent",
            "version": 1,
            "locale": "de",
            "title": "Einwilligung zur Verarbeitung der Sozialversicherungsnummer",
            "public_url": null,
            "requires_reacceptance": true,
            "is_active": true,
            "requires_consent": true,
            "accepted_count": 1,
            "pending_count": 15,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef86-70be-8580-5a1ac1757a9a",
            "document_type": "cookie_policy",
            "document_label": "Cookie Policy",
            "version": 1,
            "locale": "de",
            "title": "Cookie-Richtlinie",
            "public_url": null,
            "requires_reacceptance": false,
            "is_active": true,
            "requires_consent": false,
            "accepted_count": 0,
            "pending_count": 0,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef88-722e-9c3a-fa64bb1c2833",
            "document_type": "cookie_policy",
            "document_label": "Cookie Policy",
            "version": 1,
            "locale": "en",
            "title": "Cookie Policy",
            "public_url": null,
            "requires_reacceptance": false,
            "is_active": true,
            "requires_consent": false,
            "accepted_count": 0,
            "pending_count": 0,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef8a-7286-ada6-84f13beb905f",
            "document_type": "imprint",
            "document_label": "Imprint",
            "version": 1,
            "locale": "de",
            "title": "Impressum",
            "public_url": null,
            "requires_reacceptance": false,
            "is_active": true,
            "requires_consent": false,
            "accepted_count": 0,
            "pending_count": 0,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        {
            "id": "019fd2b7-ef8c-70fe-970a-72fcd401d198",
            "document_type": "imprint",
            "document_label": "Imprint",
            "version": 1,
            "locale": "en",
            "title": "Imprint",
            "public_url": null,
            "requires_reacceptance": false,
            "is_active": true,
            "requires_consent": false,
            "accepted_count": 0,
            "pending_count": 0,
            "published_at": "2026-08-05T16:18:17+00:00",
            "created_at": "2026-08-05T16:18:17+00:00"
        }
    ]
}
 

requires authentication

Publish a new version of a legal document (terms, privacy/Datenschutz, AÜG info, …). The new version becomes active and supersedes the previous one; the activation is recorded in the immutable audit trail.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/legal/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"document_type\": \"Beispieltext\",
    \"content\": \"Beispieltext\",
    \"locale\": \"Beispieltext\",
    \"title\": \"Frage zur Abrechnung\",
    \"public_url\": \"Beispieltext\",
    \"requires_reacceptance\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/legal/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "document_type": "Beispieltext",
    "content": "Beispieltext",
    "locale": "Beispieltext",
    "title": "Frage zur Abrechnung",
    "public_url": "Beispieltext",
    "requires_reacceptance": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/legal/documents';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'document_type' => 'Beispieltext',
            'content' => 'Beispieltext',
            'locale' => 'Beispieltext',
            'title' => 'Frage zur Abrechnung',
            'public_url' => 'Beispieltext',
            'requires_reacceptance' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/legal/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "document_type": "Beispieltext",
    "content": "Beispieltext",
    "locale": "Beispieltext",
    "title": "Frage zur Abrechnung",
    "public_url": "Beispieltext",
    "requires_reacceptance": true
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

requires authentication

For a legal document type, return its current version and which active organisations have accepted it vs. are still pending.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/acceptance-status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/acceptance-status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/acceptance-status';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/acceptance-status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "39ffedb7-32cf-41e0-aef3-b746247e7fb1",
        "timestamp": "2026-08-21T05:14:36.806720Z"
    }
}
 

requires authentication

Show a single legal document version, including its full content.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f0593-c509-716b-8a38-d51a1a7de274" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f0593-c509-716b-8a38-d51a1a7de274"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f0593-c509-716b-8a38-d51a1a7de274';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/legal/documents/019f0593-c509-716b-8a38-d51a1a7de274');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "id": "019fd2b7-ef68-7312-a1a3-6b14820cf9e2",
        "document_type": "privacy_policy",
        "version": 1,
        "locale": "de",
        "title": "Datenschutzerklärung",
        "content": "# Datenschutzerklärung\n\n**Stand: 05.08.2026**\n**Version: 1.0**\n\n---\n\n## 1. Verantwortlicher\n\n**Flexxer**\nMusterstraße 1\n1010 Wien, Österreich\n\n- **Firmenbuchnummer:** FN 123456x\n- **UID-Nummer:** ATU12345678\n- **Telefon:** +43 1 234 5678\n- **E-Mail:** privacy@flexxr.at\n- **Website:** https://flexxr.at\n\n### 1.1 Datenschutzbeauftragter\n\nFür Fragen zum Datenschutz erreichen Sie unseren Datenschutzbeauftragten unter:\n- **E-Mail:** dpo@flexxr.at\n\n---\n\n## 2. Rechtsgrundlagen der Verarbeitung\n\nWir verarbeiten Ihre personenbezogenen Daten ausschließlich auf Basis der folgenden Rechtsgrundlagen gemäß **Datenschutz-Grundverordnung (DSGVO)** und dem **österreichischen Datenschutzgesetz (DSG)**:\n\n| Rechtsgrundlage | DSGVO Artikel | Anwendungsbereich |\n|-----------------|---------------|-------------------|\n| Vertragserfüllung | Art. 6 Abs. 1 lit. b | Durchführung des Arbeits-/Überlassungsvertrags |\n| Rechtliche Verpflichtung | Art. 6 Abs. 1 lit. c | ASVG-Meldungen, Lohnsteuer, AÜG-Compliance |\n| Berechtigtes Interesse | Art. 6 Abs. 1 lit. f | Betrugsprävention, IT-Sicherheit |\n| Einwilligung | Art. 6 Abs. 1 lit. a | Marketing, optionale Datenverarbeitung |\n\n---\n\n## 3. Kategorien personenbezogener Daten\n\n### 3.1 Stammdaten\n- Vor- und Nachname\n- Geburtsdatum und Geburtsort\n- Staatsangehörigkeit\n- Geschlecht\n- Familienstand\n\n### 3.2 Kontaktdaten\n- E-Mail-Adresse\n- Telefonnummer\n- Wohnadresse\n\n### 3.3 Beschäftigungsdaten\n- Sozialversicherungsnummer (SVNR) gemäß ASVG\n- Steuernummer\n- Bankverbindung (IBAN)\n- Qualifikationen und Zertifikate\n- Arbeitszeitaufzeichnungen\n\n### 3.4 Besondere Kategorien (Art. 9 DSGVO)\n- Gesundheitsdaten (nur bei medizinischen Bescheinigungen)\n- Gewerkschaftszugehörigkeit (nur bei KV-Anwendung)\n\nDiese Daten werden nur mit ausdrücklicher Einwilligung oder aufgrund gesetzlicher Verpflichtung verarbeitet.\n\n---\n\n## 4. Zwecke der Verarbeitung\n\n### 4.1 Vertragserfüllung\n- Durchführung des Arbeitsvertrags gemäß **AÜG § 11**\n- Vermittlung von Arbeitseinsätzen\n- Arbeitszeiterfassung und Abrechnung\n- Lohn- und Gehaltsabrechnung\n\n### 4.2 Gesetzliche Verpflichtungen\n- ELDA-Meldungen an die ÖGK (gemäß **ASVG §§ 33-34**)\n- Lohnsteuerabzug und Meldung an das Finanzamt (**EStG**)\n- Aufbewahrungspflichten (**BAO § 132**: 7 Jahre für Geschäftsunterlagen)\n- AÜG-Meldepflichten an die Gewerbebehörde\n\n### 4.3 Berechtigte Interessen\n- Betrugsprävention und Identitätsprüfung\n- IT-Sicherheit und Systemstabilität\n- Qualitätssicherung und Prozessoptimierung\n\n---\n\n## 5. Empfänger der Daten\n\nIhre Daten werden an folgende Kategorien von Empfängern übermittelt:\n\n| Empfänger | Zweck | Rechtsgrundlage |\n|-----------|-------|-----------------|\n| ÖGK (Österreichische Gesundheitskasse) | Sozialversicherungsmeldungen | ASVG §§ 33-34 |\n| Finanzamt | Lohnsteuer, Lohnzettel | EStG § 84 |\n| Beschäftiger (Kundenunternehmen) | Arbeitseinsatz | AÜG § 12 |\n| Lohnverrechnungsdienstleister | Gehaltsabrechnung | Auftragsverarbeitung |\n| IT-Dienstleister | Hosting, Cloud-Services | Auftragsverarbeitung |\n\n---\n\n## 6. Speicherdauer\n\n| Datenkategorie | Speicherdauer | Rechtsgrundlage |\n|----------------|---------------|-----------------|\n| Lohnabrechnungsunterlagen | 7 Jahre | BAO § 132 |\n| Arbeitszeitaufzeichnungen | 3 Jahre nach Ende des Arbeitsverhältnisses | AZG § 26 |\n| SV-Meldungen | 7 Jahre | ASVG |\n| Bewerbungsunterlagen (bei Ablehnung) | 6 Monate | DSGVO Art. 17 |\n| Vertragsdokumente | 30 Jahre (Verjährungsfrist) | ABGB § 1489 |\n\n---\n\n## 7. Ihre Rechte nach DSGVO\n\nSie haben folgende Rechte bezüglich Ihrer personenbezogenen Daten:\n\n### 7.1 Auskunftsrecht (Art. 15 DSGVO)\nSie haben das Recht, eine Bestätigung darüber zu verlangen, ob personenbezogene Daten verarbeitet werden, und gegebenenfalls Auskunft über diese Daten zu erhalten.\n\n### 7.2 Recht auf Berichtigung (Art. 16 DSGVO)\nSie haben das Recht, unrichtige personenbezogene Daten unverzüglich berichtigen zu lassen.\n\n### 7.3 Recht auf Löschung (Art. 17 DSGVO)\nSie haben das Recht, die Löschung Ihrer Daten zu verlangen, sofern keine gesetzlichen Aufbewahrungspflichten entgegenstehen.\n\n### 7.4 Recht auf Einschränkung (Art. 18 DSGVO)\nSie haben das Recht, die Einschränkung der Verarbeitung zu verlangen.\n\n### 7.5 Recht auf Datenübertragbarkeit (Art. 20 DSGVO)\nSie haben das Recht, Ihre Daten in einem strukturierten, gängigen und maschinenlesbaren Format zu erhalten.\n\n### 7.6 Widerspruchsrecht (Art. 21 DSGVO)\nSie haben das Recht, gegen die Verarbeitung Ihrer Daten Widerspruch einzulegen.\n\n### 7.7 Recht auf Widerruf der Einwilligung (Art. 7 Abs. 3 DSGVO)\nSie haben das Recht, erteilte Einwilligungen jederzeit zu widerrufen.\n\n---\n\n## 8. Beschwerderecht bei der Aufsichtsbehörde\n\nSie haben das Recht, eine Beschwerde bei der zuständigen Aufsichtsbehörde einzureichen:\n\n**Österreichische Datenschutzbehörde**\nBarichgasse 40-42\n1030 Wien\n\n- **E-Mail:** dsb@dsb.gv.at\n- **Website:** https://www.dsb.gv.at\n\n---\n\n## 9. Automatisierte Entscheidungsfindung\n\nEs findet keine automatisierte Entscheidungsfindung im Sinne von **Art. 22 DSGVO** statt, die rechtliche Wirkung entfaltet oder Sie in ähnlicher Weise erheblich beeinträchtigt.\n\n---\n\n## 10. Drittlandübermittlung\n\nEine Übermittlung Ihrer Daten in Drittländer (außerhalb des EWR) erfolgt nur:\n- Auf Basis von Angemessenheitsbeschlüssen der EU-Kommission\n- Unter Verwendung von EU-Standardvertragsklauseln\n- Mit Ihrer ausdrücklichen Einwilligung\n\n---\n\n## 11. Änderungen dieser Datenschutzerklärung\n\nWir behalten uns vor, diese Datenschutzerklärung anzupassen, um sie an geänderte Rechtslagen oder bei Änderungen unserer Dienste anzupassen. Die aktuelle Version finden Sie stets unter: https://flexxr.at/legal/privacy_policy\n\n---\n\n## 12. Kontakt\n\nBei Fragen zum Datenschutz wenden Sie sich bitte an:\n\n**Flexxer**\nMusterstraße 1, 1010 Wien\nE-Mail: privacy@flexxr.at\n",
        "content_hash": "7d28b9a8ddc190e477a58a1d95a0a50d2f0428e19594df2dfff7742c94e784ff",
        "public_url": null,
        "requires_reacceptance": true,
        "is_active": true,
        "published_at": "2026-08-05T16:18:17+00:00",
        "created_at": "2026-08-05T16:18:17+00:00"
    }
}
 

Support

List Support Tickets

requires authentication

The tickets visible to the caller, newest first, optionally narrowed by status.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/support/tickets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/support/tickets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/support/tickets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "tickets": [
            {
                "id": "019fd2b8-28b2-7322-b70c-bc7d76debfc3",
                "ticket_number": "TKT-717858",
                "subject": "Nihil autem neque blanditiis accusantium eaque.",
                "category": "general",
                "category_label": "Allgemein",
                "status": "open",
                "status_label": "Offen",
                "priority": 3,
                "requester_type": "company",
                "requester_name": "Caritas",
                "assignee_name": null,
                "message_count": 0,
                "last_message_at": null,
                "created_at": "2026-08-05T16:18:32+00:00"
            },
            {
                "id": "019fd2b8-2e3b-7096-97f9-96d4a481c803",
                "ticket_number": "TKT-969367",
                "subject": "Totam autem repellendus quasi iste debitis fugiat.",
                "category": "complaint",
                "category_label": "Beschwerde",
                "status": "open",
                "status_label": "Offen",
                "priority": 2,
                "requester_type": "worker",
                "requester_name": "Anna Neuling",
                "assignee_name": null,
                "message_count": 0,
                "last_message_at": null,
                "created_at": "2026-08-05T16:18:33+00:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 2
        }
    }
}
 

Request      

GET api/v1/admin/support/tickets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Show a Support Ticket

requires authentication

One ticket with its message thread. Internal notes are never included.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "id": "019fd2b8-28b2-7322-b70c-bc7d76debfc3",
        "ticket_number": "TKT-717858",
        "subject": "Nihil autem neque blanditiis accusantium eaque.",
        "category": "general",
        "category_label": "Allgemein",
        "status": "open",
        "status_label": "Offen",
        "priority": 3,
        "requester_type": "company",
        "requester_name": "Caritas",
        "assignee_name": null,
        "message_count": 0,
        "last_message_at": null,
        "created_at": "2026-08-05T16:18:32+00:00",
        "description": "Quo necessitatibus et tempora esse nemo. Omnis et molestias sed expedita cupiditate.\n\nQuidem mollitia et reprehenderit enim quidem est. Temporibus et blanditiis iusto officia neque molestiae quidem. Adipisci suscipit sit temporibus voluptatum porro molestiae omnis. Rem fuga ratione ratione odio esse odio modi.",
        "resolution_notes": null,
        "related_entity_type": null,
        "related_entity_id": null,
        "assigned_to": null,
        "first_response_at": null,
        "resolved_at": null,
        "messages": []
    }
}
 

Request      

GET api/v1/admin/support/tickets/{ticketId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

The ticket. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Assign Ticket

requires authentication

POST /admin/support/tickets/{ticketId}/assign — assign to an agent, defaulting to the acting admin.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/assign" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"admin_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/assign"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "admin_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/assign';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'admin_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/assign');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "admin_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "id": "019fd2b8-28b2-7322-b70c-bc7d76debfc3",
        "assigned_to": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "assignee_name": "Super Admin"
    }
}
 

Request      

POST api/v1/admin/support/tickets/{ticketId}/assign

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

admin_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reply to a Support Ticket

requires authentication

Appends a message to an existing ticket.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"message\": \"Bitte um Rueckmeldung zur naechsten Schicht.\",
    \"is_internal\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_internal": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'message' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
            'is_internal' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_internal": true
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your message has been sent.",
    "data": {
        "message": {
            "id": "019fd2b8-539c-7266-9498-f92f5406de4a",
            "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
            "sender_type": "agent",
            "sender_name": "Super Admin",
            "is_internal": true,
            "attachments": null,
            "created_at": "2026-08-05T16:18:43+00:00"
        }
    }
}
 

Request      

POST api/v1/admin/support/tickets/{ticketId}/messages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

The ticket. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

message   string     

Example: Bitte um Rueckmeldung zur naechsten Schicht.

is_internal   boolean  optional    

Example: true

Update Ticket Status

requires authentication

POST /admin/support/tickets/{ticketId}/status — transition a ticket. "open" reopens a resolved/closed ticket. Moving to "resolved" notifies the requester and stamps resolved_at; "closed" stamps closed_at.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"Beispieltext\",
    \"resolution_notes\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "Beispieltext",
    "resolution_notes": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/status';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'Beispieltext',
            'resolution_notes' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "Beispieltext",
    "resolution_notes": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/support/tickets/{ticketId}/status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

status   string     

Example: Beispieltext

resolution_notes   string  optional    

Example: Beispieltext

Update Ticket Priority

requires authentication

POST /admin/support/tickets/{ticketId}/priority — 1 (low) .. 4 (urgent).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/priority" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"priority\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/priority"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "priority": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/priority';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'priority' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/priority');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "priority": 1
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "id": "019fd2b8-28b2-7322-b70c-bc7d76debfc3",
        "priority": 1
    }
}
 

Request      

POST api/v1/admin/support/tickets/{ticketId}/priority

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

priority   integer     

Example: 1

Organization API

Endpoints for the mobile application (employee users). All endpoints (except authentication) require a valid Bearer token with 2FA verification.

Authentication

Register Organization

Registers a new organization account. All new organizations begin with a pending_verification status. They can authenticate and receive tokens immediately but the org.approved middleware blocks access to protected dashboard endpoints until an admin verifies them.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/register" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Water Foundation\",
    \"email\": \"org@example.com\",
    \"password\": \"OrgPassword123\",
    \"password_confirmation\": \"OrgPassword123\",
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\",
    \"description\": \"A charity focused on water access in developing nations.\",
    \"website\": \"https:\\/\\/waterfoundation.org\",
    \"address\": \"b\",
    \"address_line2\": \"n\",
    \"uid_number\": \"gzmiyvdljnikhway\",
    \"legal_form\": \"ag\",
    \"firmenbuchnummer\": \"kcmyuwpwlvqwrsit\",
    \"contact_person_name\": \"c\",
    \"contact_person_phone\": \"p\",
    \"industry_code\": \"scqldz\",
    \"company_size\": \"11-50\",
    \"terms_accepted\": true,
    \"avv_accepted\": true,
    \"postal_code\": \"snrwtujwvlxjklqp\",
    \"city\": \"p\",
    \"country\": \"w\",
    \"iso_country_code\": \"qb\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/register"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Water Foundation",
    "email": "org@example.com",
    "password": "OrgPassword123",
    "password_confirmation": "OrgPassword123",
    "country_code": "+43",
    "phone_number": "6641234567",
    "description": "A charity focused on water access in developing nations.",
    "website": "https:\/\/waterfoundation.org",
    "address": "b",
    "address_line2": "n",
    "uid_number": "gzmiyvdljnikhway",
    "legal_form": "ag",
    "firmenbuchnummer": "kcmyuwpwlvqwrsit",
    "contact_person_name": "c",
    "contact_person_phone": "p",
    "industry_code": "scqldz",
    "company_size": "11-50",
    "terms_accepted": true,
    "avv_accepted": true,
    "postal_code": "snrwtujwvlxjklqp",
    "city": "p",
    "country": "w",
    "iso_country_code": "qb"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/register';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Water Foundation',
            'email' => 'org@example.com',
            'password' => 'OrgPassword123',
            'password_confirmation' => 'OrgPassword123',
            'country_code' => '+43',
            'phone_number' => '6641234567',
            'description' => 'A charity focused on water access in developing nations.',
            'website' => 'https://waterfoundation.org',
            'address' => 'b',
            'address_line2' => 'n',
            'uid_number' => 'gzmiyvdljnikhway',
            'legal_form' => 'ag',
            'firmenbuchnummer' => 'kcmyuwpwlvqwrsit',
            'contact_person_name' => 'c',
            'contact_person_phone' => 'p',
            'industry_code' => 'scqldz',
            'company_size' => '11-50',
            'terms_accepted' => true,
            'avv_accepted' => true,
            'postal_code' => 'snrwtujwvlxjklqp',
            'city' => 'p',
            'country' => 'w',
            'iso_country_code' => 'qb',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/register');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Water Foundation",
    "email": "org@example.com",
    "password": "OrgPassword123",
    "password_confirmation": "OrgPassword123",
    "country_code": "+43",
    "phone_number": "6641234567",
    "description": "A charity focused on water access in developing nations.",
    "website": "https:\/\/waterfoundation.org",
    "address": "b",
    "address_line2": "n",
    "uid_number": "gzmiyvdljnikhway",
    "legal_form": "ag",
    "firmenbuchnummer": "kcmyuwpwlvqwrsit",
    "contact_person_name": "c",
    "contact_person_phone": "p",
    "industry_code": "scqldz",
    "company_size": "11-50",
    "terms_accepted": true,
    "avv_accepted": true,
    "postal_code": "snrwtujwvlxjklqp",
    "city": "p",
    "country": "w",
    "iso_country_code": "qb"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Success):


{
    "status": "CREATED",
    "message": "Organization registered successfully.",
    "data": {
        "token": "1|abcdef123456...",
        "organization": {
            "id": "org-uuid",
            "name": "Water Foundation",
            "email": "org@example.com",
            "status": "pending_verification"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, Email Already Registered):


{
    "status": "EMAIL_ALREADY_EXISTS",
    "message": "This email is already registered.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "This email is already registered."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "name",
            "message": "The name field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Organization name (max 255). Example: Water Foundation

email   string     

Organization email (max 255). Example: org@example.com

password   string     

Password (min 8, must be confirmed). Example: OrgPassword123

password_confirmation   string     

Password confirmation. Example: OrgPassword123

country_code   string     

Country calling code. Example: +43

phone_number   string     

Phone number (digits only). Example: 6641234567

description   string     

Organization description (max 5000). Example: A charity focused on water access in developing nations.

website   string  optional    

optional Organization website URL. Example: https://waterfoundation.org

address   string  optional    

Must not be greater than 255 characters. Example: b

address_line2   string  optional    

Must not be greater than 255 characters. Example: n

uid_number   string  optional    

Must match the regex /^ATU\d{8}$|^[A-Z]{2}[0-9A-Za-z+*.]{2,12}$/. Must not be greater than 20 characters. Example: gzmiyvdljnikhway

legal_form   string  optional    

Example: ag

Must be one of:
  • gmbh
  • ag
  • og
  • kg
  • gmbh_co_kg
  • einzelunternehmen
  • verein
  • sonstige
firmenbuchnummer   string  optional    

This field is required when legal_form is gmbh, ag, og, kg, or gmbh_co_kg. Must not be greater than 20 characters. Example: kcmyuwpwlvqwrsit

contact_person_name   string  optional    

Must not be greater than 150 characters. Example: c

contact_person_phone   string  optional    

Must not be greater than 30 characters. Example: p

industry_code   string  optional    

Must not be greater than 10 characters. Example: scqldz

company_size   string  optional    

Example: 11-50

Must be one of:
  • 1-10
  • 11-50
  • 51-250
  • 250+
terms_accepted   boolean     

Must be accepted. Example: true

avv_accepted   boolean     

Must be accepted. Example: true

postal_code   string  optional    

Must not be greater than 20 characters. Example: snrwtujwvlxjklqp

city   string  optional    

Must not be greater than 100 characters. Example: p

country   string  optional    

Must not be greater than 100 characters. Example: w

iso_country_code   string  optional    

Must be 2 characters. Example: qb

Login Organization

Authenticates an organization against the organization guard and issues a Sanctum token. If the organization has 2FA enabled a limited challenge token is returned. Note: the org.approved middleware is not applied here; it is applied on the protected routes. An organization in any status may log in — they simply cannot access protected endpoints until approved.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"org@example.com\",
    \"country_code\": \"+43\",
    \"phone_number\": \"6641234567\",
    \"password\": \"OrgPassword123\",
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\",
    \"remember_me\": false
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "org@example.com",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "OrgPassword123",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH",
    "remember_me": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'org@example.com',
            'country_code' => '+43',
            'phone_number' => '6641234567',
            'password' => 'OrgPassword123',
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
            'remember_me' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/login');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "org@example.com",
    "country_code": "+43",
    "phone_number": "6641234567",
    "password": "OrgPassword123",
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH",
    "remember_me": false
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Login successful.",
    "data": {
        "token": "10|4Ucu33DSapxQkfLaVou7QWZebPNBb0UxdrbLmEPj11373a72",
        "organization": {
            "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "name": "Caritas",
            "slug": "caritas-4v1ues",
            "email": "caritas-portal@flexxr.eu.cc",
            "country_code": "+43",
            "phone_number": "6641234567",
            "contact_person_name": null,
            "contact_person_phone": null,
            "iso_country_code": "AT",
            "phone_e164": "+436641234567",
            "description": "Caritas test organisation used for end-to-end portal QA. Safe to delete.",
            "logo_url": null,
            "website": "https://www.caritas.at",
            "address": "Albrechtskreithgasse 19-21",
            "address_line2": null,
            "postal_code": null,
            "city": "Vienna",
            "country": "Austria",
            "status": "active",
            "verified_at": "2026-08-05T16:18:17.000000Z",
            "is_verified": true,
            "total_raised": null,
            "created_at": "2026-08-05T16:18:17.000000Z",
            "platform_fee_flat_cents": {},
            "firmenbuchnummer": {},
            "uid_number": {},
            "uid_validation_status": {},
            "gisa_status": {},
            "payout_method": {},
            "stripe_connect_account_id": {},
            "connect_charges_enabled": {},
            "connect_payouts_enabled": {},
            "connect_details_submitted": {},
            "connect_requirements_due": {},
            "connect_country_code": {},
            "connect_external_account": {}
        }
    },
    "errors": null,
    "meta": {
        "request_id": "8a92c2fa-6959-4cc4-b267-75087ca278f0",
        "timestamp": "2026-08-05T16:18:37.432170Z"
    }
}
 

Example response (401, 2FA Required):


{
    "status": "TWO_FACTOR_REQUIRED",
    "message": "Two-factor authentication is required.",
    "data": {
        "token": "2|challenge-token...",
        "two_factor_method": "totp"
    },
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Invalid Credentials):


{
    "status": "INVALID_CREDENTIALS",
    "message": "The provided credentials are incorrect.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The provided credentials are incorrect."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The email field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (423, Account Temporarily Locked):


{
    "status": "ACCOUNT_TEMPORARILY_LOCKED",
    "message": "Account temporarily locked. Try again in 15 minutes.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "Account temporarily locked. Try again in 15 minutes."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Organization email address. Example: org@example.com

country_code   string  optional    

Country code with + prefix. Required together with phone_number for phone login. This field is required when phone_number is present. Must match the regex /^+\d{1,4}$/. Must not be greater than 5 characters. Example: +43

phone_number   string  optional    

Phone number without country code. Required together with country_code for phone login. This field is required when country_code is present. Must match the regex /^\d{6,15}$/. Must not be greater than 15 characters. Example: 6641234567

password   string     

Plain-text password (min 8 characters). Example: OrgPassword123

code   string  optional    

Optional 2FA one-time code (TOTP or email OTP) for single-step 2FA login. Example: 123456

recovery_code   string  optional    

Optional 2FA recovery code in place of code. Example: ABCD-1234-EFGH

remember_me   boolean  optional    

Example: false

Member Login

Authenticates an OrganizationMember using the linked User account's email and password. Issues a Sanctum token scoped to the OrganizationMember model so the member can access the organisation routes independently of the Organisation principal.

When the member has 2FA enrolled a limited challenge token is returned and the client must complete the two-factor flow via the challenge endpoint before accessing protected resources.

Only members belonging to organisations with an Active status are permitted to reach the approved route group; this action itself does not block based on org status (mirrors LoginOrganizationAction).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/member/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jane@example.com\",
    \"password\": \"SecureP4ssword!\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/member/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "jane@example.com",
    "password": "SecureP4ssword!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/member/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jane@example.com',
            'password' => 'SecureP4ssword!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/member/login');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "jane@example.com",
    "password": "SecureP4ssword!"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Login successful.",
    "data": {
        "token": "1|abc...",
        "member": {
            "id": "uuid",
            "name": "Jane Doe",
            "email": "jane@example.com",
            "role": "member",
            "organization_id": "org-uuid"
        },
        "acl": {
            "scope": "org-member",
            "presets": [
                "viewer"
            ],
            "permissions": [
                "profile.view",
                "team.view"
            ]
        }
    }
}
 

Example response (401, 2FA Required):


{
    "status": "TWO_FACTOR_REQUIRED",
    "message": "Two-factor authentication is required.",
    "data": {
        "token": "2|challenge-token...",
        "two_factor_method": "totp"
    }
}
 

Example response (401, Invalid Credentials):


{
    "status": "INVALID_CREDENTIALS",
    "message": "The provided credentials are incorrect."
}
 

Request      

POST api/v1/org/auth/member/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address of the member's User account. Example: jane@example.com

password   string     

Password for the User account. Example: SecureP4ssword!

Two-Factor Authentication

Regenerate Recovery Codes

requires authentication

Generates a fresh set of single-use 2FA recovery codes, immediately invalidating the previous batch. Codes are returned in plain text exactly once — they cannot be retrieved again after this response. Requires 2FA to be enabled and confirmed.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/security/recovery-codes/regenerate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/security/recovery-codes/regenerate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/security/recovery-codes/regenerate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/security/recovery-codes/regenerate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Recovery codes regenerated. Store them securely — they will not be shown again.",
    "data": {
        "recovery_codes": [
            "GDLZX-N9LP9",
            "VPZAB-DJHRO",
            "73VKY-CYUZU",
            "NB6X9-M7H4O",
            "HKRRW-SKJ2S",
            "9FCNW-CBQ2E",
            "SW6IZ-ZHVXH",
            "DC8MJ-NXVZF"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "1d2ba116-7b58-49a0-a5a9-cdf820d35b7d",
        "timestamp": "2026-08-05T16:18:38.620100Z"
    }
}
 

Example response (403, 2FA Not Enabled):


{
    "status": "FORBIDDEN",
    "message": "Two-factor authentication is not enabled.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/security/recovery-codes/regenerate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Profile

Show Profile

requires authentication

Returns the full profile of the organisation the caller belongs to. Invited members receive their company's profile, not their own membership record.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/profile" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/profile"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/profile';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/profile');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Profile retrieved successfully.",
    "data": {
        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "name": "Caritas",
        "slug": "caritas-4v1ues",
        "email": "caritas-portal@flexxr.eu.cc",
        "country_code": "+43",
        "phone_number": "6641234567",
        "contact_person_name": null,
        "contact_person_phone": null,
        "iso_country_code": "AT",
        "phone_e164": "+436641234567",
        "description": "Caritas test organisation used for end-to-end portal QA. Safe to delete.",
        "logo_url": null,
        "website": "https://www.caritas.at",
        "address": "Albrechtskreithgasse 19-21",
        "address_line2": null,
        "postal_code": null,
        "city": "Vienna",
        "country": "Austria",
        "status": "active",
        "verified_at": "2026-08-05T16:18:17.000000Z",
        "is_verified": true,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:17.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "e435791d-4abf-49f0-ba30-628b75142d31",
        "timestamp": "2026-08-05T16:18:37.730963Z"
    }
}
 

Request      

GET api/v1/org/profile

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Org Profile

requires authentication

Updates mutable profile fields on the authenticated organisation. Only provided fields are applied; absent keys leave existing values untouched. Password and bank-detail changes have dedicated endpoints.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/profile" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Hope Foundation\",
    \"country_code\": \"+49\",
    \"phone_number\": \"1234567890\",
    \"contact_person_name\": \"u\",
    \"contact_person_phone\": \"w\",
    \"description\": \"A non-profit dedicated to helping communities.\",
    \"website\": \"https:\\/\\/hopefoundation.org\",
    \"address\": \"Musterstr. 1\",
    \"address_line2\": \"i\",
    \"postal_code\": \"tcpscqldzsnrwtuj\",
    \"city\": \"Berlin\",
    \"country\": \"Germany\",
    \"iso_country_code\": \"lx\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/profile"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Hope Foundation",
    "country_code": "+49",
    "phone_number": "1234567890",
    "contact_person_name": "u",
    "contact_person_phone": "w",
    "description": "A non-profit dedicated to helping communities.",
    "website": "https:\/\/hopefoundation.org",
    "address": "Musterstr. 1",
    "address_line2": "i",
    "postal_code": "tcpscqldzsnrwtuj",
    "city": "Berlin",
    "country": "Germany",
    "iso_country_code": "lx"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/profile';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Hope Foundation',
            'country_code' => '+49',
            'phone_number' => '1234567890',
            'contact_person_name' => 'u',
            'contact_person_phone' => 'w',
            'description' => 'A non-profit dedicated to helping communities.',
            'website' => 'https://hopefoundation.org',
            'address' => 'Musterstr. 1',
            'address_line2' => 'i',
            'postal_code' => 'tcpscqldzsnrwtuj',
            'city' => 'Berlin',
            'country' => 'Germany',
            'iso_country_code' => 'lx',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/profile');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Hope Foundation",
    "country_code": "+49",
    "phone_number": "1234567890",
    "contact_person_name": "u",
    "contact_person_phone": "w",
    "description": "A non-profit dedicated to helping communities.",
    "website": "https:\/\/hopefoundation.org",
    "address": "Musterstr. 1",
    "address_line2": "i",
    "postal_code": "tcpscqldzsnrwtuj",
    "city": "Berlin",
    "country": "Germany",
    "iso_country_code": "lx"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Profile updated successfully.",
    "data": {
        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "name": "Hope Foundation",
        "slug": "caritas-4v1ues",
        "email": "caritas-portal@flexxr.eu.cc",
        "country_code": "+43",
        "phone_number": "13456789",
        "contact_person_name": "u",
        "contact_person_phone": "w",
        "iso_country_code": "lx",
        "phone_e164": "+4313456789",
        "description": "A non-profit dedicated to helping communities.",
        "logo_url": null,
        "website": "https://hopefoundation.org",
        "address": "Musterstr. 1",
        "address_line2": "i",
        "postal_code": "tcpscqldzsnrwtuj",
        "city": "Berlin",
        "country": "Germany",
        "status": "active",
        "verified_at": "2026-08-05T16:18:17.000000Z",
        "is_verified": true,
        "total_raised": null,
        "created_at": "2026-08-05T16:18:17.000000Z",
        "platform_fee_flat_cents": null,
        "firmenbuchnummer": null,
        "uid_number": null,
        "uid_validation_status": null,
        "payout_method": null,
        "stripe_connect_account_id": null,
        "connect_charges_enabled": null,
        "connect_payouts_enabled": null,
        "connect_details_submitted": null,
        "connect_requirements_due": null,
        "connect_country_code": null
    },
    "errors": null,
    "meta": {
        "request_id": "4205ec1a-adaa-4fa2-a3e2-8cf17da36ec3",
        "timestamp": "2026-08-05T16:18:37.748449Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "Validation failed.",
    "data": null,
    "errors": [
        {
            "field": "phone_number",
            "message": "Phone number format is invalid."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

PUT api/v1/org/profile

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string  optional    

nullable Organization name (max 255). Example: Hope Foundation

country_code   string  optional    

nullable Phone country code with leading plus (e.g. +49). Example: +49

phone_number   string  optional    

nullable Phone number digits only (max 20). Example: 1234567890

contact_person_name   string  optional    

Must not be greater than 150 characters. Example: u

contact_person_phone   string  optional    

Must not be greater than 30 characters. Example: w

description   string  optional    

nullable Organization description (max 5000). Example: A non-profit dedicated to helping communities.

website   string  optional    

nullable Website URL (max 255). Example: https://hopefoundation.org

address   string  optional    

nullable Street address (max 500). Example: Musterstr. 1

address_line2   string  optional    

Must not be greater than 255 characters. Example: i

postal_code   string  optional    

Must not be greater than 20 characters. Example: tcpscqldzsnrwtuj

city   string  optional    

nullable City name (max 100). Example: Berlin

country   string  optional    

nullable Country name (max 100). Example: Germany

iso_country_code   string  optional    

Must be 2 characters. Example: lx

Change Org Password

requires authentication

Changes the authenticated organisation's login password. Requires the current password to be provided and verified before the new one is persisted. Pass logout_other_devices: true to revoke every other Sanctum session on the organisation account.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/profile/password" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"current_password\": \"OldPassword123!\",
    \"password\": \"NewPassword456!\",
    \"logout_other_devices\": true,
    \"password_confirmation\": \"NewPassword456!\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/profile/password"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "current_password": "OldPassword123!",
    "password": "NewPassword456!",
    "logout_other_devices": true,
    "password_confirmation": "NewPassword456!"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/profile/password';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'current_password' => 'OldPassword123!',
            'password' => 'NewPassword456!',
            'logout_other_devices' => true,
            'password_confirmation' => 'NewPassword456!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/profile/password');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "current_password": "OldPassword123!",
    "password": "NewPassword456!",
    "logout_other_devices": true,
    "password_confirmation": "NewPassword456!"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password changed successfully.",
    "data": {
        "revoked_sessions": 0
    },
    "errors": null,
    "meta": {
        "request_id": "dff4679b-ef2a-4fb9-94a7-48ac27abddfa",
        "timestamp": "2026-08-05T16:18:38.445813Z"
    }
}
 

Example response (422, Wrong Password):


{
    "status": "VALIDATION_ERROR",
    "message": "The current password is incorrect.",
    "data": null,
    "errors": [
        {
            "field": "current_password",
            "message": "The current password is incorrect."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Password Reuse):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "You cannot reuse your last 3 passwords."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

PUT api/v1/org/profile/password

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

current_password   string     

Current password for verification. Example: OldPassword123!

password   string     

New password (min 12 chars, 1 upper, 1 lower, 1 digit, must be confirmed). Example: NewPassword456!

logout_other_devices   boolean  optional    

nullable Revoke all other active sessions if true. Example: true

password_confirmation   string     

Password confirmation. Example: NewPassword456!

Get Me

requires authentication

Returns the authenticated organisation's profile (name, email, avatar).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/me" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "name": "Caritas",
        "email": "caritas-portal@flexxr.eu.cc",
        "email_verified_at": null,
        "avatar_url": null,
        "created_at": "2026-08-05T16:18:17+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "a45b6707-e121-40d2-a89f-c692e5d6c2bb",
        "timestamp": "2026-08-05T16:18:38.460687Z"
    }
}
 

Request      

GET api/v1/org/me

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Me

requires authentication

Example request:
curl --request PATCH \
    "https://backend-dev.flexxr.at/api/v1/org/me" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"ACME Foundation\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "ACME Foundation"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'ACME Foundation',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "ACME Foundation"
};

  final response = await http.patch(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Profile updated successfully.",
    "data": {
        "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
        "name": "ACME Foundation",
        "email": "caritas-portal@flexxr.eu.cc"
    },
    "errors": null,
    "meta": {
        "request_id": "83d44c1f-73cb-42d8-b29f-3ec083b2921a",
        "timestamp": "2026-08-05T16:18:38.470674Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "name",
            "message": "The name may not be greater than 255 characters."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

PATCH api/v1/org/me

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string  optional    

Optional. Display name. Example: ACME Foundation

Dashboard

Get Org Dashboard Stats

requires authentication

Returns aggregated dashboard statistics for the authenticated organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/dashboard/stats" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/dashboard/stats"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/dashboard/stats';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/dashboard/stats');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Dashboard statistics retrieved successfully.",
    "data": {
        "users_count": 1,
        "pending_invitations": 0,
        "active_shifts": 4,
        "upcoming_jobs": 4,
        "pending_applicants": 227,
        "monthly_spend_cents": 37747,
        "attendance_rate_percent": 13,
        "open_positions": 96
    },
    "errors": null,
    "meta": {
        "request_id": "52103b61-fa39-4660-988a-b44356150484",
        "timestamp": "2026-08-05T16:18:38.749634Z"
    }
}
 

Request      

GET api/v1/org/dashboard/stats

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

requires authentication

Daily activity series for the dashboard charts: shifts and applications over the last 14 days (zero-filled) plus the current vacancy fill across open jobs.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/dashboard/trends" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/dashboard/trends"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/dashboard/trends';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/dashboard/trends');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "days": [
            {
                "date": "2026-07-23",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-24",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-25",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-26",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-27",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-28",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-29",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-30",
                "shifts": 0,
                "completed": 0,
                "applications": 0
            },
            {
                "date": "2026-07-31",
                "shifts": 0,
                "completed": 0,
                "applications": 28
            },
            {
                "date": "2026-08-01",
                "shifts": 0,
                "completed": 0,
                "applications": 111
            },
            {
                "date": "2026-08-02",
                "shifts": 0,
                "completed": 0,
                "applications": 77
            },
            {
                "date": "2026-08-03",
                "shifts": 1,
                "completed": 1,
                "applications": 12
            },
            {
                "date": "2026-08-04",
                "shifts": 1,
                "completed": 0,
                "applications": 4
            },
            {
                "date": "2026-08-05",
                "shifts": 1,
                "completed": 0,
                "applications": 1
            }
        ],
        "vacancies": {
            "total": 96,
            "filled": 0
        }
    }
}
 

Team

List Team Members

requires authentication

Returns the list of team members for the authenticated organisation: the principal plus every accepted member. Each member row includes the presets array of currently-assigned preset keys.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/team" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "name": "Caritas",
            "email": "caritas-portal@flexxr.eu.cc",
            "role": "owner",
            "status": "active",
            "avatar_url": null,
            "is_self": true,
            "is_primary_admin": true,
            "is_active": true,
            "presets": [],
            "joined_at": "2026-08-05T16:18:17+00:00",
            "last_active_at": "2026-08-05T16:18:33+00:00"
        },
        {
            "id": "019fd2b8-285e-707c-865a-ffbe4b08f730",
            "name": "Nikolas Klocko",
            "email": "omacejkovic@example.net",
            "role": "admin",
            "status": "active",
            "avatar_url": null,
            "is_self": false,
            "is_primary_admin": false,
            "is_active": true,
            "presets": [],
            "joined_at": "2026-08-05T16:18:32+00:00",
            "last_active_at": null
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "89fd6657-4c88-42a0-aeea-ecc0ef10dd35",
        "timestamp": "2026-08-05T16:18:38.792198Z"
    }
}
 

Request      

GET api/v1/org/team

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Assignable Presets

requires authentication

Returns the list of presets an org may assign to its team members. Used by the dashboard's "Manage roles" dialog.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/team/presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/presets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "key": "owner",
            "label": "Owner",
            "description": "Primary company owner with unrestricted access to all company features.",
            "permissions": [
                "applicants.accept",
                "applicants.reject",
                "applicants.view",
                "assignments.manage",
                "assignments.view",
                "attendance.approve-hours",
                "attendance.report-noshow",
                "attendance.view",
                "audit.view",
                "gdpr.request",
                "invoices.download",
                "invoices.view",
                "jobs.configure-assignment",
                "jobs.create",
                "jobs.delete",
                "jobs.edit",
                "jobs.publish",
                "jobs.view",
                "messages.access",
                "notifications.preferences.manage",
                "payments.manage",
                "payments.view",
                "profile.edit",
                "profile.view",
                "ratings.create",
                "ratings.view",
                "reports.export",
                "reports.view",
                "requests.decide",
                "requests.view",
                "shifts.adjust",
                "shifts.cancel",
                "shifts.check-in",
                "shifts.dispute-time",
                "shifts.generate-qr",
                "shifts.report-signature-refusal",
                "shifts.view",
                "support.create-ticket",
                "support.view-tickets",
                "team.invite",
                "team.manage-roles",
                "team.remove",
                "team.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "org-admin",
            "label": "Admin",
            "description": "Company settings, team, jobs, applicants, attendance, and the company wallet.",
            "permissions": [
                "applicants.accept",
                "applicants.reject",
                "applicants.view",
                "assignments.manage",
                "assignments.view",
                "attendance.approve-hours",
                "attendance.report-noshow",
                "attendance.view",
                "audit.view",
                "jobs.configure-assignment",
                "jobs.create",
                "jobs.delete",
                "jobs.edit",
                "jobs.publish",
                "jobs.view",
                "messages.access",
                "notifications.preferences.manage",
                "payments.manage",
                "payments.view",
                "profile.edit",
                "profile.view",
                "ratings.create",
                "ratings.view",
                "reports.export",
                "reports.view",
                "requests.decide",
                "requests.view",
                "shifts.adjust",
                "shifts.cancel",
                "shifts.check-in",
                "shifts.dispute-time",
                "shifts.generate-qr",
                "shifts.report-signature-refusal",
                "shifts.view",
                "support.create-ticket",
                "support.view-tickets",
                "team.invite",
                "team.manage-roles",
                "team.remove",
                "team.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "recruiter",
            "label": "Recruiter",
            "description": "Create and manage job listings, review and manage applicants.",
            "permissions": [
                "applicants.accept",
                "applicants.reject",
                "applicants.view",
                "jobs.configure-assignment",
                "jobs.create",
                "jobs.delete",
                "jobs.edit",
                "jobs.publish",
                "jobs.view",
                "messages.access",
                "ratings.view",
                "reports.view",
                "shifts.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "shift-manager",
            "label": "Shift Manager",
            "description": "Manage attendance, approve hours, and handle no-shows.",
            "permissions": [
                "assignments.view",
                "attendance.approve-hours",
                "attendance.report-noshow",
                "attendance.view",
                "messages.access",
                "ratings.create",
                "ratings.view",
                "reports.view",
                "requests.decide",
                "requests.view",
                "shifts.adjust",
                "shifts.cancel",
                "shifts.check-in",
                "shifts.dispute-time",
                "shifts.generate-qr",
                "shifts.report-signature-refusal",
                "shifts.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "finance",
            "label": "Finance",
            "description": "View and manage invoices and payments.",
            "permissions": [
                "invoices.download",
                "invoices.view",
                "payments.manage",
                "payments.view",
                "reports.export",
                "reports.view",
                "requests.decide",
                "requests.view"
            ],
            "category": null,
            "highlights": [],
            "risk_level": null
        },
        {
            "key": "co-admin",
            "label": "Co-admin",
            "description": "Full access except team role management and member removal. Use \"admin\" instead.",
            "permissions": [
                "applicants.accept",
                "applicants.reject",
                "applicants.view",
                "assignments.manage",
                "assignments.view",
                "attendance.approve-hours",
                "attendance.report-noshow",
                "attendance.view",
                "audit.view",
                "gdpr.request",
                "invoices.download",
                "invoices.view",
                "jobs.configure-assignment",
                "jobs.create",
                "jobs.delete",
                "jobs.edit",
                "jobs.publish",
                "jobs.view",
                "messages.access",
                "notifications.preferences.manage",
                "payments.manage",
                "payments.view",
                "profile.edit",
                "profile.view",
                "ratings.create",
                "ratings.view",
                "reports.export",
                "reports.view",
                "requests.decide",
                "requests.view",
                "shifts.adjust",
                "shifts.cancel",
                "shifts.check-in",
                "shifts.dispute-time",
                "shifts.generate-qr",
                "shifts.report-signature-refusal",
                "shifts.view",
                "support.create-ticket",
                "support.view-tickets",
                "team.invite",
                "team.view"
            ],
            "category": "organization_admin",
            "highlights": [
                "presets.co-admin.highlights.almost_full_access",
                "presets.co-admin.highlights.manage_team",
                "presets.co-admin.highlights.no_ownership_transfer"
            ],
            "risk_level": "high"
        },
        {
            "key": "manager",
            "label": "Manager",
            "description": "Operational team management. Use \"admin\" preset instead.",
            "permissions": [
                "applicants.accept",
                "applicants.reject",
                "applicants.view",
                "attendance.approve-hours",
                "attendance.report-noshow",
                "attendance.view",
                "jobs.configure-assignment",
                "jobs.create",
                "jobs.delete",
                "jobs.edit",
                "jobs.publish",
                "jobs.view",
                "notifications.preferences.manage",
                "profile.edit",
                "profile.view",
                "ratings.create",
                "ratings.view",
                "shifts.adjust",
                "shifts.cancel",
                "shifts.check-in",
                "shifts.dispute-time",
                "shifts.generate-qr",
                "shifts.report-signature-refusal",
                "shifts.view",
                "team.invite",
                "team.view"
            ],
            "category": "operations",
            "highlights": [
                "presets.manager.highlights.edit_profile",
                "presets.manager.highlights.invite_members",
                "presets.manager.highlights.manage_notifications"
            ],
            "risk_level": "medium"
        },
        {
            "key": "viewer",
            "label": "Viewer",
            "description": "Read-only access. Cannot make any changes.",
            "permissions": [
                "applicants.view",
                "attendance.view",
                "invoices.view",
                "jobs.view",
                "profile.view",
                "ratings.view",
                "reports.view",
                "shifts.view",
                "team.view"
            ],
            "category": "read_only",
            "highlights": [
                "presets.viewer.highlights.read_only",
                "presets.viewer.highlights.no_changes"
            ],
            "risk_level": "low"
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "c429e72e-0dee-402b-939b-bc1bce7762b4",
        "timestamp": "2026-08-05T16:18:38.809465Z"
    }
}
 

Request      

GET api/v1/org/team/presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Assignable Roles and Presets

requires authentication

The roles a member may hold and the presets that may be granted to them — everything the portal's team screens need to render a role picker and the permission matrix without hardcoding either list.

Owner is excluded: ownership is the organisation account itself and is never assigned to a member.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/team/roles" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/roles"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/roles';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/roles');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "roles": [
            {
                "id": "admin",
                "label": "Admin",
                "description": "Company settings, team, jobs, applicants, attendance, and the company wallet.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "assignments.manage",
                    "assignments.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "audit.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "messages.access",
                    "notifications.preferences.manage",
                    "payments.manage",
                    "payments.view",
                    "profile.edit",
                    "profile.view",
                    "ratings.create",
                    "ratings.view",
                    "reports.export",
                    "reports.view",
                    "requests.decide",
                    "requests.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view",
                    "support.create-ticket",
                    "support.view-tickets",
                    "team.invite",
                    "team.manage-roles",
                    "team.remove",
                    "team.view"
                ]
            },
            {
                "id": "recruiter",
                "label": "Recruiter",
                "description": "Create and manage job listings, review and manage applicants.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "messages.access",
                    "ratings.view",
                    "reports.view",
                    "shifts.view"
                ]
            },
            {
                "id": "shift_manager",
                "label": "Shift Manager",
                "description": "Manage attendance, approve hours, and handle no-shows.",
                "permissions": [
                    "assignments.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "messages.access",
                    "ratings.create",
                    "ratings.view",
                    "reports.view",
                    "requests.decide",
                    "requests.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view"
                ]
            },
            {
                "id": "finance",
                "label": "Finance",
                "description": "View and manage invoices and payments.",
                "permissions": [
                    "invoices.download",
                    "invoices.view",
                    "payments.manage",
                    "payments.view",
                    "reports.export",
                    "reports.view",
                    "requests.decide",
                    "requests.view"
                ]
            }
        ],
        "presets": [
            {
                "id": "owner",
                "label": "Owner",
                "description": "Primary company owner with unrestricted access to all company features.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "assignments.manage",
                    "assignments.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "audit.view",
                    "gdpr.request",
                    "invoices.download",
                    "invoices.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "messages.access",
                    "notifications.preferences.manage",
                    "payments.manage",
                    "payments.view",
                    "profile.edit",
                    "profile.view",
                    "ratings.create",
                    "ratings.view",
                    "reports.export",
                    "reports.view",
                    "requests.decide",
                    "requests.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view",
                    "support.create-ticket",
                    "support.view-tickets",
                    "team.invite",
                    "team.manage-roles",
                    "team.remove",
                    "team.view"
                ],
                "category": null,
                "risk_level": null
            },
            {
                "id": "org-admin",
                "label": "Admin",
                "description": "Company settings, team, jobs, applicants, attendance, and the company wallet.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "assignments.manage",
                    "assignments.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "audit.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "messages.access",
                    "notifications.preferences.manage",
                    "payments.manage",
                    "payments.view",
                    "profile.edit",
                    "profile.view",
                    "ratings.create",
                    "ratings.view",
                    "reports.export",
                    "reports.view",
                    "requests.decide",
                    "requests.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view",
                    "support.create-ticket",
                    "support.view-tickets",
                    "team.invite",
                    "team.manage-roles",
                    "team.remove",
                    "team.view"
                ],
                "category": null,
                "risk_level": null
            },
            {
                "id": "recruiter",
                "label": "Recruiter",
                "description": "Create and manage job listings, review and manage applicants.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "messages.access",
                    "ratings.view",
                    "reports.view",
                    "shifts.view"
                ],
                "category": null,
                "risk_level": null
            },
            {
                "id": "shift-manager",
                "label": "Shift Manager",
                "description": "Manage attendance, approve hours, and handle no-shows.",
                "permissions": [
                    "assignments.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "messages.access",
                    "ratings.create",
                    "ratings.view",
                    "reports.view",
                    "requests.decide",
                    "requests.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view"
                ],
                "category": null,
                "risk_level": null
            },
            {
                "id": "finance",
                "label": "Finance",
                "description": "View and manage invoices and payments.",
                "permissions": [
                    "invoices.download",
                    "invoices.view",
                    "payments.manage",
                    "payments.view",
                    "reports.export",
                    "reports.view",
                    "requests.decide",
                    "requests.view"
                ],
                "category": null,
                "risk_level": null
            },
            {
                "id": "co-admin",
                "label": "Co-admin",
                "description": "Full access except team role management and member removal. Use \"admin\" instead.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "assignments.manage",
                    "assignments.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "audit.view",
                    "gdpr.request",
                    "invoices.download",
                    "invoices.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "messages.access",
                    "notifications.preferences.manage",
                    "payments.manage",
                    "payments.view",
                    "profile.edit",
                    "profile.view",
                    "ratings.create",
                    "ratings.view",
                    "reports.export",
                    "reports.view",
                    "requests.decide",
                    "requests.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view",
                    "support.create-ticket",
                    "support.view-tickets",
                    "team.invite",
                    "team.view"
                ],
                "category": "organization_admin",
                "risk_level": "high"
            },
            {
                "id": "manager",
                "label": "Manager",
                "description": "Operational team management. Use \"admin\" preset instead.",
                "permissions": [
                    "applicants.accept",
                    "applicants.reject",
                    "applicants.view",
                    "attendance.approve-hours",
                    "attendance.report-noshow",
                    "attendance.view",
                    "jobs.configure-assignment",
                    "jobs.create",
                    "jobs.delete",
                    "jobs.edit",
                    "jobs.publish",
                    "jobs.view",
                    "notifications.preferences.manage",
                    "profile.edit",
                    "profile.view",
                    "ratings.create",
                    "ratings.view",
                    "shifts.adjust",
                    "shifts.cancel",
                    "shifts.check-in",
                    "shifts.dispute-time",
                    "shifts.generate-qr",
                    "shifts.report-signature-refusal",
                    "shifts.view",
                    "team.invite",
                    "team.view"
                ],
                "category": "operations",
                "risk_level": "medium"
            },
            {
                "id": "viewer",
                "label": "Viewer",
                "description": "Read-only access. Cannot make any changes.",
                "permissions": [
                    "applicants.view",
                    "attendance.view",
                    "invoices.view",
                    "jobs.view",
                    "profile.view",
                    "ratings.view",
                    "reports.view",
                    "shifts.view",
                    "team.view"
                ],
                "category": "read_only",
                "risk_level": "low"
            }
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "936accb8-3905-4985-9c5c-f3310da1c479",
        "timestamp": "2026-08-05T16:18:38.827828Z"
    }
}
 

Request      

GET api/v1/org/team/roles

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Team Invitations

requires authentication

Returns all pending invitations for the authenticated organisation, newest first.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/team/invitations" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/invitations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/invitations');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "37de59fd-ab57-4351-b751-f42c1912b877",
        "timestamp": "2026-08-05T16:18:38.848580Z"
    }
}
 

Request      

GET api/v1/org/team/invitations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Invite Team Member

requires authentication

Creates a pending team invitation and sends an accept-link email to the invitee.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"member@example.com\",
    \"name\": \"Jane Smith\",
    \"preset_key\": \"viewer\",
    \"role\": \"member\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "member@example.com",
    "name": "Jane Smith",
    "preset_key": "viewer",
    "role": "member"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/invitations';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'member@example.com',
            'name' => 'Jane Smith',
            'preset_key' => 'viewer',
            'role' => 'member',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/invitations');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "member@example.com",
    "name": "Jane Smith",
    "preset_key": "viewer",
    "role": "member"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "CREATED",
    "message": "Invitation sent.",
    "data": {
        "id": "019fd2b8-b25f-72cd-80f6-76a3fa80752a",
        "email": "kole.murray@example.com",
        "name": null,
        "role": "member",
        "preset_key": "viewer",
        "preset_label": "Viewer",
        "status": "pending",
        "invited_by_name": "Schuppe-D'Amore",
        "invited_at": "2026-08-05T16:19:07+00:00",
        "expires_at": "2026-08-12T16:19:07+00:00",
        "accepted_at": null,
        "accepted_by_email": null,
        "revoked_at": null,
        "last_sent_at": "2026-08-05T16:19:07+00:00",
        "send_count": 1,
        "created_at": "2026-08-05T16:19:07+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "07ca8112-6166-4ac4-b613-5485efe2470a",
        "timestamp": "2026-08-05T16:19:07.496377Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "email",
            "message": "The email field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/team/invitations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Invitee email address (max 255). Example: member@example.com

name   string  optional    

nullable Invitee display name (max 255). Example: Jane Smith

preset_key   string     

ACL preset key for the invitee (e.g. viewer, manager, co-admin). Example: viewer

role   string  optional    

nullable Legacy role override (member, manager). Derived from preset_key when omitted. Example: member

Resend Team Invitation

requires authentication

Re-sends the accept-link email for a pending invitation, rotating the token and resetting the expiry window.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111/resend" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111/resend"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111/resend');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Invitation re-sent.",
    "data": {
        "id": "019fd2b8-affd-71e3-b323-f2b2d9a8c01b",
        "email": "zweite.kollegin@example.at",
        "name": null,
        "role": "member",
        "preset_key": "viewer",
        "preset_label": "Viewer",
        "status": "pending",
        "invited_by_name": "Franecki-Christiansen",
        "invited_at": "2026-08-05T16:19:06+00:00",
        "expires_at": "2026-08-12T16:19:06+00:00",
        "accepted_at": null,
        "accepted_by_email": null,
        "revoked_at": null,
        "last_sent_at": "2026-08-05T16:19:06+00:00",
        "send_count": 2,
        "created_at": "2026-08-05T16:19:06+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "619fc1d5-dc41-43a9-9212-7cc961686f00",
        "timestamp": "2026-08-05T16:19:06.913816Z"
    }
}
 

Example response (404, Invitation Not Found):


{
    "status": "NOT_FOUND",
    "message": "No query results for model.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/team/invitations/{invitation}/resend

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invitation   string     

Invitation UUID. Example: 661f9511-f3ac-52e5-b827-557766551111

Revoke Team Invitation

requires authentication

Revokes a pending team invitation. Accepted invitations cannot be revoked.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/invitations/661f9511-f3ac-52e5-b827-557766551111');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Invitation revoked.",
    "data": {
        "id": "019fd2b8-affd-71e3-b323-f2b2d9a8c01b",
        "email": "zweite.kollegin@example.at",
        "name": null,
        "role": "member",
        "preset_key": "viewer",
        "preset_label": "Viewer",
        "status": "revoked",
        "invited_by_name": "Franecki-Christiansen",
        "invited_at": "2026-08-05T16:19:06+00:00",
        "expires_at": "2026-08-12T16:19:06+00:00",
        "accepted_at": null,
        "accepted_by_email": null,
        "revoked_at": "2026-08-05T16:19:06+00:00",
        "last_sent_at": "2026-08-05T16:19:06+00:00",
        "send_count": 2,
        "created_at": "2026-08-05T16:19:06+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "af8be4ed-34a6-4610-a07f-38b7cc5318b3",
        "timestamp": "2026-08-05T16:19:06.936143Z"
    }
}
 

Example response (404, Invitation Not Found):


{
    "status": "NOT_FOUND",
    "message": "No query results for model.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/org/team/invitations/{invitation}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invitation   string     

Invitation UUID. Example: 661f9511-f3ac-52e5-b827-557766551111

Update Member Role

requires authentication

Updates a team member's role. Only the owning organisation may perform this action. The Owner role cannot be assigned via this endpoint — it belongs exclusively to the Organisation principal itself.

Example request:
curl --request PATCH \
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"role\": \"shift_manager\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "role": "shift_manager"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/members/architecto';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'role' => 'shift_manager',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/members/architecto');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "role": "shift_manager"
};

  final response = await http.patch(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Member role updated.",
    "data": {
        "id": "019fd2b8-285e-707c-865a-ffbe4b08f730",
        "user_id": "019fd2b8-285d-733a-9a39-9eff7782cebc",
        "role": "shift_manager",
        "joined_at": "2026-08-05T16:18:32.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "112413aa-862d-4d0d-b901-32f1e694825b",
        "timestamp": "2026-08-05T16:18:38.884959Z"
    }
}
 

Example response (403):


{
    "status": "FORBIDDEN",
    "message": "This action is unauthorized.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "This action is unauthorized."
        }
    ]
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "Cannot assign the owner role via this endpoint.",
    "data": null,
    "errors": [
        {
            "field": "role",
            "message": "Cannot assign the owner role via this endpoint."
        }
    ]
}
 

Request      

PATCH api/v1/org/team/members/{member_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

member_id   string     

The ID of the member. Example: architecto

Body Parameters

role   string     

The member's new role. Owner is refused here — ownership transfer is its own operation. Allowed: owner, admin, recruiter, shift_manager, finance. Example: shift_manager

Update Member Presets

requires authentication

Bulk-replaces the presets assigned to a team member. The request body carries an array of preset keys; presets currently held but absent from the array are revoked, presets present but not yet assigned are added. Diff and writes run inside a single transaction.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/presets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"presets\": [
        \"manager\",
        \"finance\"
    ]
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/presets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "presets": [
        "manager",
        "finance"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/presets';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'presets' => ['manager', 'finance'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/presets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "presets": [
        "manager",
        "finance"
    ]
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Member presets updated.",
    "data": {
        "member_id": "uuid",
        "presets": [
            "manager"
        ]
    },
    "errors": null
}
 

Example response (403, Forbidden):


{
    "status": "FORBIDDEN",
    "message": "This action is unauthorized."
}
 

Example response (422, Unknown preset):


{
    "status": "VALIDATION_ERROR",
    "message": "Validation failed.",
    "errors": [
        {
            "field": "presets.0",
            "message": "Unknown preset 'foo'."
        }
    ]
}
 

Request      

PUT api/v1/org/team/members/{member_id}/presets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

member_id   string     

The ID of the member. Example: architecto

Body Parameters

presets   string[]     

List of preset keys to assign. Pass an empty array to revoke all.

Deactivate Team Member

requires authentication

Soft-deactivates a team member: sets is_active = false and revokes all Sanctum tokens so any in-flight sessions are immediately invalidated. The member row is retained for audit and compliance purposes.

Guards:

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/deactivate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/deactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/deactivate';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/deactivate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Member deactivated.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "e0a4f5b9-485f-468f-a82e-e0e71a736f71",
        "timestamp": "2026-08-05T16:18:38.928851Z"
    }
}
 

Example response (403):


{
    "status": "FORBIDDEN",
    "message": "This action is unauthorized."
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "You cannot deactivate yourself."
}
 

Request      

PUT api/v1/org/team/members/{member_id}/deactivate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

member_id   string     

The ID of the member. Example: architecto

Reactivate Team Member

requires authentication

Re-enables a previously deactivated team member. The member regains access to the organisation dashboard on their next login; existing tokens were revoked at deactivation time so a fresh login is required.

Guards:

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/reactivate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/reactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/reactivate';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/members/architecto/reactivate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Member reactivated.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "6022ea0d-33c3-46cd-a043-6dbbbc198b31",
        "timestamp": "2026-08-05T16:18:38.951107Z"
    }
}
 

Example response (403):


{
    "status": "FORBIDDEN",
    "message": "This action is unauthorized."
}
 

Request      

PUT api/v1/org/team/members/{member_id}/reactivate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

member_id   string     

The ID of the member. Example: architecto

Remove Team Member

requires authentication

Removes a member from the organisation's team. The primary admin (Owner role) cannot be removed via this endpoint — ownership transfer is a separate, deliberate operation.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/team/members/architecto"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/team/members/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/team/members/architecto');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (204):

Empty response
 

Example response (403):


{
    "status": "FORBIDDEN",
    "message": "This action is unauthorized.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "This action is unauthorized."
        }
    ]
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "The organisation owner cannot be removed.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "The organisation owner cannot be removed."
        }
    ]
}
 

Request      

DELETE api/v1/org/team/members/{member_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

member_id   string     

The ID of the member. Example: architecto

Jobs

List Organization Jobs

requires authentication

List all jobs for the organization.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "jobs": [
            {
                "id": "019fd2b8-28ee-71e0-b50d-2c303c17f728",
                "title": "Kindergarten Teacher",
                "category": "warehouse",
                "category_label": "Warehouse",
                "status": "pending_review",
                "status_label": "In Prüfung",
                "status_color": "yellow",
                "location_city": "Eldahaven",
                "start_date": "2026-08-21",
                "end_date": "2026-09-15",
                "shift_start_time": "06:44:00",
                "shift_end_time": "06:04:00",
                "next_shift_date": null,
                "total_vacancies": 1,
                "filled_vacancies": 0,
                "hourly_rate_gross": 18.4,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-09-05 16:18",
                "published_at": null,
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant",
                "category": "office",
                "category_label": "Office",
                "status": "active",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "East Eliseo",
                "start_date": "2026-08-12",
                "end_date": "2026-08-19",
                "shift_start_time": "01:49:00",
                "shift_end_time": "18:57:00",
                "next_shift_date": "2026-08-05",
                "total_vacancies": 5,
                "filled_vacancies": 0,
                "hourly_rate_gross": 32.08,
                "applications_count": 1,
                "shifts_count": 1,
                "application_deadline": "2026-09-05 16:18",
                "published_at": null,
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-28e8-73a4-94d6-5a423c21f064",
                "title": "Foundry Mold and Coremaker",
                "category": "software",
                "category_label": "Software",
                "status": "draft",
                "status_label": "Entwurf",
                "status_color": "gray",
                "location_city": "South Zachery",
                "start_date": "2026-08-29",
                "end_date": "2026-09-25",
                "shift_start_time": "16:30:00",
                "shift_end_time": "14:33:00",
                "next_shift_date": null,
                "total_vacancies": 10,
                "filled_vacancies": 0,
                "hourly_rate_gross": 29.86,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-09-05 16:18",
                "published_at": null,
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-2705-73fd-a401-002956f1f401",
                "title": "Fahrer*in Zustelldienst Wien Nord",
                "category": "logistics",
                "category_label": "Logistics",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-14",
                "end_date": "2026-08-15",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 4,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-08-12 00:00",
                "published_at": "2026-08-05 04:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-2700-724b-adc5-2be8957e0590",
                "title": "Kassierer*in Supermarkt Floridsdorf",
                "category": "supermarket",
                "category_label": "Grocery",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-11",
                "end_date": "2026-08-12",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 5,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13.5,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-08-09 00:00",
                "published_at": "2026-08-05 08:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-26fb-7142-b3a3-df10c3591584",
                "title": "Küchenhilfe Streetfood Festival Naschmarkt",
                "category": "catering",
                "category_label": "Catering",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-13",
                "end_date": "2026-08-14",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 6,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-08-11 00:00",
                "published_at": "2026-08-05 13:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-270f-715d-a3a0-76c4c314e3ff",
                "title": "Verkäufer*in Elektronik Mediamarkt Vösendorf",
                "category": "retail",
                "category_label": "Retail",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Vösendorf",
                "start_date": "2026-08-20",
                "end_date": "2026-08-21",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 7,
                "filled_vacancies": 0,
                "hourly_rate_gross": 14,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-08-18 00:00",
                "published_at": "2026-08-04 16:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-2599-7355-a7f1-dcb185b05faa",
                "title": "Rezeptionist*in Hotel Sacher",
                "category": "hotel",
                "category_label": "Hospitality",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-17",
                "end_date": "2026-08-19",
                "shift_start_time": "09:00:00",
                "shift_end_time": "17:00:00",
                "next_shift_date": null,
                "total_vacancies": 4,
                "filled_vacancies": 0,
                "hourly_rate_gross": 17.5,
                "applications_count": 51,
                "shifts_count": 0,
                "application_deadline": "2026-08-14 00:00",
                "published_at": "2026-07-31 16:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-24b0-700a-ae2a-142f5e48aead",
                "title": "Lagerhelfer*in Logistikzentrum Inzersdorf",
                "category": "warehouse",
                "category_label": "Warehouse",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-12",
                "end_date": "2026-08-14",
                "shift_start_time": "22:00:00",
                "shift_end_time": "06:00:00",
                "next_shift_date": null,
                "total_vacancies": 15,
                "filled_vacancies": 0,
                "hourly_rate_gross": 14.8,
                "applications_count": 29,
                "shifts_count": 0,
                "application_deadline": "2026-08-09 00:00",
                "published_at": "2026-08-01 16:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-270a-71f9-a13c-ef188105041c",
                "title": "Reinigungskraft Hotel Ibis Mariahilf",
                "category": "cleaning",
                "category_label": "Cleaning",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-16",
                "end_date": "2026-08-17",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 3,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13.2,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-08-14 00:00",
                "published_at": "2026-08-04 22:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-21c8-72cc-80b1-47fd13450522",
                "title": "Barkeeper*in Sommernacht Open Air",
                "category": "gastro",
                "category_label": "Gastronomy",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-15",
                "end_date": "2026-08-17",
                "shift_start_time": "22:00:00",
                "shift_end_time": "06:00:00",
                "next_shift_date": null,
                "total_vacancies": 8,
                "filled_vacancies": 0,
                "hourly_rate_gross": 16,
                "applications_count": 38,
                "shifts_count": 0,
                "application_deadline": "2026-08-12 00:00",
                "published_at": "2026-07-31 16:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-22cf-71c5-8edd-5e3ce9d29e8b",
                "title": "Promoter*in Vienna Marathon",
                "category": "event",
                "category_label": "Events",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-26",
                "end_date": "2026-08-28",
                "shift_start_time": "09:00:00",
                "shift_end_time": "17:00:00",
                "next_shift_date": null,
                "total_vacancies": 20,
                "filled_vacancies": 0,
                "hourly_rate_gross": 13.5,
                "applications_count": 62,
                "shifts_count": 0,
                "application_deadline": "2026-08-23 00:00",
                "published_at": "2026-07-31 16:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-1f86-7376-9a3a-c072ab5675cb",
                "title": "Service-Mitarbeiter*in Wiener Prater Festival",
                "category": "gastro",
                "category_label": "Gastronomy",
                "status": "published",
                "status_label": "Aktiv",
                "status_color": "green",
                "location_city": "Wien",
                "start_date": "2026-08-19",
                "end_date": "2026-08-21",
                "shift_start_time": "09:00:00",
                "shift_end_time": "17:00:00",
                "next_shift_date": null,
                "total_vacancies": 12,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15.5,
                "applications_count": 45,
                "shifts_count": 0,
                "application_deadline": "2026-08-16 00:00",
                "published_at": "2026-07-31 16:18",
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-1dcd-734a-a041-f809f58e95fb",
                "title": "IT Supportkraft Vertretung",
                "category": "it_services",
                "category_label": "IT Services",
                "status": "pending_review",
                "status_label": "In Prüfung",
                "status_color": "yellow",
                "location_city": "Wien",
                "start_date": "2026-08-20",
                "end_date": "2026-09-04",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 2,
                "filled_vacancies": 0,
                "hourly_rate_gross": 22,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-08-15 16:18",
                "published_at": null,
                "created_at": "2026-08-05 16:18"
            },
            {
                "id": "019fd2b8-1dcb-73f4-9257-e7fa42fe8514",
                "title": "Sommerfest Catering",
                "category": "catering",
                "category_label": "Catering",
                "status": "pending_review",
                "status_label": "In Prüfung",
                "status_color": "yellow",
                "location_city": "Wien",
                "start_date": "2026-09-24",
                "end_date": "2026-09-25",
                "shift_start_time": "08:00:00",
                "shift_end_time": "16:00:00",
                "next_shift_date": null,
                "total_vacancies": 12,
                "filled_vacancies": 0,
                "hourly_rate_gross": 15,
                "applications_count": 0,
                "shifts_count": 0,
                "application_deadline": "2026-09-22 16:18",
                "published_at": null,
                "created_at": "2026-08-05 16:18"
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 2,
            "per_page": 15,
            "total": 18
        }
    }
}
 

Request      

GET api/v1/org/jobs

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Organization Job Categories

requires authentication

List the active job categories available to an organization when creating a job or filtering the jobs list. Returns a flat, ordered list keyed by slug, each carrying its authoritative KV minimum hourly wage (LSD-BG equal treatment) so the portal validates pay against the same floor the backend enforces at submit.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/categories" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/categories"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/categories';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/categories');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "categories": [
            {
                "code": "gastro",
                "label": "Gastronomy",
                "icon": "utensils",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "hotel",
                "label": "Hospitality",
                "icon": "hotel",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "food_service",
                "label": "Food Service",
                "icon": "utensils",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "catering",
                "label": "Catering",
                "icon": "utensils",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "retail",
                "label": "Retail",
                "icon": "shopping-cart",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "supermarket",
                "label": "Grocery",
                "icon": "shopping-cart",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "pharmacy",
                "label": "Pharmacy",
                "icon": "heart-pulse",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "production",
                "label": "Production",
                "icon": "industry",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "warehouse",
                "label": "Warehouse",
                "icon": "industry",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "logistics",
                "label": "Logistics",
                "icon": "truck",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "manufacturing",
                "label": "Manufacturing",
                "icon": "industry",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "transportation",
                "label": "Transportation",
                "icon": "truck",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "event",
                "label": "Events",
                "icon": "calendar-star",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "security",
                "label": "Security",
                "icon": "shield",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "office",
                "label": "Office",
                "icon": "briefcase",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "reception",
                "label": "Reception",
                "icon": "briefcase",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "call_center",
                "label": "Call Center",
                "icon": "briefcase",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "customer_service",
                "label": "Customer Service",
                "icon": "briefcase",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "software",
                "label": "Software",
                "icon": "code",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "it_services",
                "label": "IT Services",
                "icon": "code",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "cleaning",
                "label": "Cleaning",
                "icon": "broom",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "facility",
                "label": "Facility Management",
                "icon": "broom",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "care",
                "label": "Care",
                "icon": "heart-pulse",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "healthcare",
                "label": "Healthcare",
                "icon": "heart-pulse",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "construction",
                "label": "Construction",
                "icon": "building",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "real_estate",
                "label": "Real Estate",
                "icon": "building",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "graphic_design",
                "label": "Graphic Design",
                "icon": "palette",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "media",
                "label": "Media",
                "icon": "palette",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "education",
                "label": "Education",
                "icon": "graduation-cap",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "finance",
                "label": "Finance",
                "icon": "chart-line",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "agriculture",
                "label": "Agriculture",
                "icon": "leaf",
                "min_hourly_cents": null,
                "rate_source": null
            },
            {
                "code": "other",
                "label": "Other",
                "icon": "ellipsis",
                "min_hourly_cents": null,
                "rate_source": null
            }
        ]
    }
}
 

Request      

GET api/v1/org/jobs/categories

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Job Document Types

requires authentication

The catalog of worker document types a company can require as an application prerequisite (e.g. work permit, driver licence). Only types an admin has left company-selectable are offered. Backs the required-documents multiselect on the create/edit job form.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/document-types" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/document-types"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/document-types';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/document-types');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "document_types": [
            {
                "code": "passport",
                "label": "Passport"
            },
            {
                "code": "id_card",
                "label": "ID card"
            },
            {
                "code": "driver_license",
                "label": "Driver's licence"
            },
            {
                "code": "work_permit",
                "label": "Work permit"
            },
            {
                "code": "residence_permit",
                "label": "Residence permit"
            },
            {
                "code": "rot_weiss_rot_karte",
                "label": "Rot-Weiß-Rot card"
            },
            {
                "code": "blue_card",
                "label": "EU Blue Card"
            },
            {
                "code": "bank_statement",
                "label": "Bank statement"
            },
            {
                "code": "proof_of_address",
                "label": "Proof of address"
            },
            {
                "code": "social_insurance_card",
                "label": "Social insurance record"
            },
            {
                "code": "tax_document",
                "label": "Tax document"
            },
            {
                "code": "certification",
                "label": "Certification"
            },
            {
                "code": "health_certificate",
                "label": "Health certificate"
            },
            {
                "code": "criminal_record_check",
                "label": "Criminal record check"
            },
            {
                "code": "other",
                "label": "Other"
            }
        ]
    }
}
 

Request      

GET api/v1/org/jobs/document-types

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Job

requires authentication

Create a new job listing for the organization.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been created successfully.",
    "data": {
        "job": {
            "id": "019fd2b8-6bb5-7113-b62c-7434d5f44733",
            "title": "Lagermitarbeiter (m/w/d)",
            "status": "draft",
            "status_label": "Entwurf",
            "created_at": "2026-08-05 16:18:49"
        }
    }
}
 

Request      

POST api/v1/org/jobs

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Organization Job

requires authentication

Get job details for the organization.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "title": "Accountant",
            "description": "Ut nostrum rerum ut alias quia libero excepturi. Voluptatum et aut non quia quisquam molestias. Perferendis qui hic unde.",
            "category": "office",
            "category_label": "Office",
            "status": "active",
            "status_label": "Aktiv",
            "status_color": "green",
            "shift_type": "night",
            "shift_type_label": "Nachtschicht",
            "location": {
                "name": "Barrows, Christiansen and Jones",
                "address": "586 Tremaine Row",
                "city": "East Eliseo",
                "postal_code": "10547-3697",
                "lat": 46.931383,
                "lng": 11.998284
            },
            "schedule": {
                "start_date": "2026-08-12",
                "end_date": "2026-08-19",
                "shift_start_time": "01:49:00",
                "shift_end_time": "18:57:00"
            },
            "vacancies": {
                "total": 5,
                "filled": 0,
                "available": 5
            },
            "compensation": {
                "hourly_rate_gross": 32.08,
                "supplements": {
                    "night": 0.7,
                    "weekend": 3.86
                },
                "estimated_total_gross": 0
            },
            "requirements": {
                "qualifications": [],
                "dress_code": "Tempore necessitatibus quia illo suscipit.",
                "equipment_provided": "Adipisci fugiat doloremque atque consectetur necessitatibus sunt cumque."
            },
            "contact": {
                "parking_info": null,
                "special_instructions": null
            },
            "deadlines": {
                "application_deadline": "2026-09-05 16:18",
                "confirmation_deadline": "2026-08-16 10:20",
                "sign_window_hours": 24
            },
            "statistics": {
                "applications_count": 1,
                "shifts_count": 1
            },
            "assignment": {
                "method": "manual",
                "auto_reject_after_hours": null
            },
            "required_documents": [
                {
                    "code": "passport",
                    "name": "Reisepass"
                },
                {
                    "code": "driver_license",
                    "name": "Führerschein"
                }
            ],
            "custom_document_requests": [],
            "planned_shifts": [
                {
                    "id": "019fd2b8-28d5-7103-b951-9f6ff1d94556",
                    "shift_date": "2026-08-05",
                    "start_time": "08:00",
                    "end_time": "16:00",
                    "break_minutes": 30,
                    "workers_needed": 2,
                    "shift_manager_member_id": null,
                    "shift_manager_name": null,
                    "notes": null,
                    "dress_code": null,
                    "contact_person_name": null,
                    "contact_person_phone": null,
                    "meeting_point": null,
                    "geofence": null,
                    "application_deadline": null
                }
            ],
            "review": {
                "submitted_at": null,
                "reviewed_at": null,
                "rejection_note": null
            },
            "timestamps": {
                "published_at": null,
                "filled_at": null,
                "cancelled_at": null,
                "cancellation_reason": null,
                "created_at": "2026-08-05 16:18",
                "updated_at": "2026-08-05 16:18"
            }
        }
    }
}
 

Request      

GET api/v1/org/jobs/{jobId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Submit Job For Review

requires authentication

Submit a draft (or previously rejected) job for admin review. Funds for every shift + promotion are blocked from the wallet at submit time, so an admin only ever reviews a job the company can afford. The job then waits in pending_review until an admin approves (→ active) or rejects it.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/submit" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/submit"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/submit';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/submit');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been submitted for admin review.",
    "data": {
        "job": {
            "id": "019fd2b8-6bb5-7113-b62c-7434d5f44733",
            "status": "pending_review",
            "status_label": "In Prüfung",
            "submitted_at": "2026-08-05 16:18"
        },
        "compliance_flags": []
    }
}
 

Request      

POST api/v1/org/jobs/{jobId}/submit

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Close Job

requires authentication

Close a job (cancel or mark as filled).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/close" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"filled\",
    \"cancellation_reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/close"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "filled",
    "cancellation_reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/close';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'filled',
            'cancellation_reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/close');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "filled",
    "cancellation_reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been marked as filled.",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "status": "filled",
            "status_label": "Besetzt"
        }
    }
}
 

Request      

POST api/v1/org/jobs/{jobId}/close

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Allowed: filled, cancelled. Example: filled

cancellation_reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

List Applicants

requires authentication

List all applicants for a specific job.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "applicants": [
            {
                "id": "019fd2b8-76c6-72bb-a2d9-98ed4934fb02",
                "status": "pending",
                "status_label": "Eingereicht",
                "applied_at": "2026-08-05 16:18",
                "has_scheduling_conflict": false,
                "acknowledged_conflict": false,
                "shortlisted_at": null,
                "selected_at": null,
                "rejected_at": null,
                "notes": null,
                "match_score": 78,
                "employee": {
                    "id": "019fd2b8-7352-72bc-9514-ac7713e5c225",
                    "name": "Fae G.",
                    "identity_revealed": false,
                    "email": null,
                    "phone": null
                },
                "profile": {
                    "nationality": "AT",
                    "city": "North Asa",
                    "profile_completion_percentage": 86,
                    "avg_rating": null,
                    "total_shifts_completed": 0
                }
            }
        ],
        "summary": {
            "total": 1,
            "by_status": {
                "pending": 1,
                "shortlisted": 0,
                "selected": 0,
                "rejected": 0
            }
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/org/jobs/{jobId}/applicants

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

List Job Shifts

requires authentication

List a job's shift SLOTS (the shifts table — one row per planned slot), each with how many of its workers are filled. Per-worker rows live under the assignments endpoints, not here. A job has few slots, so the collection is loaded once and filtered/paginated in memory.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "title": "Accountant",
            "status": "active",
            "status_label": "Aktiv"
        },
        "shifts": [
            {
                "id": "019fd2b8-28d5-7103-b951-9f6ff1d94556",
                "shift_date": "2026-08-05",
                "start_time": "08:00",
                "end_time": "16:00",
                "break_minutes": 30,
                "workers_needed": 2,
                "filled_count": 0,
                "open_count": 2,
                "status": "scheduled",
                "status_label": "Geplant",
                "shift_manager": null,
                "notes": null
            }
        ],
        "summary": {
            "total_slots": 1,
            "total_workers_needed": 2,
            "total_filled": 0,
            "by_status": {
                "scheduled": 1,
                "in_progress": 0,
                "completed": 0,
                "cancelled": 0
            }
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 50,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/org/jobs/{jobId}/shifts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

List Job Contracts

requires authentication

List the leasing (Überlassung) contracts tied to a job's shifts, with the worker, signing status and a download URL for the signed PDF. Powers the Contracts tab on the redesigned job detail page.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contracts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contracts"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contracts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contracts');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "job": {
            "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
            "title": "Accountant"
        },
        "contracts": [],
        "summary": {
            "total": 0,
            "signed": 0,
            "pending": 0
        }
    }
}
 

Request      

GET api/v1/org/jobs/{jobId}/contracts

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Get Job Contract Template

requires authentication

A job's contract template for the job editor: the job-level override when one exists ("customized"), otherwise the effective template inherited from the organisation default (or the built-in body) — plus the merge fields.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "template": {
            "name": "Überlassungsvertrag",
            "body_html": "<h2>Arbeitskraft</h2>\n<p>{worker_name}, geboren am {worker_birth_date}, {worker_address}</p>\n<h2>Beschäftiger</h2>\n<p>{organization_name}, {organization_address}</p>\n<h2>Überlassung</h2>\n<p>Tätigkeit: {job_title}<br>Einsatzort: {location}<br>Zeitraum: {start_date} – {end_date}<br>Entgelt (brutto/Std.): {hourly_rate}<br>Kollektivvertrag: {collective_agreement}</p>\n<p>Für die Dauer der Überlassung gelten gemäß § 10 AÜG die im Beschäftigerbetrieb für vergleichbare Arbeitnehmer geltenden wesentlichen Arbeits- und Beschäftigungsbedingungen. Rechtsgrundlage: {legal_basis}.</p>",
            "version": 0,
            "is_active": false,
            "is_customized": false
        },
        "merge_fields": [
            "contract_number",
            "worker_name",
            "worker_birth_date",
            "worker_nationality",
            "worker_address",
            "organization_name",
            "organization_address",
            "job_title",
            "job_description",
            "location",
            "start_date",
            "end_date",
            "hourly_rate",
            "collective_agreement",
            "legal_basis"
        ]
    }
}
 

Request      

GET api/v1/org/jobs/{jobId}/contract-template

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Update Job Contract Template

requires authentication

Save (or reset) a job's contract template override. inherit: true deletes the job-level row so the job falls back to the organisation default; otherwise the body is upserted, seeded from the resolved parent (the organisation default, or the built-in body) the first time a job is customised.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Beispieltext\",
    \"body_html\": \"Bitte um Rueckmeldung zur naechsten Schicht.\",
    \"is_active\": true,
    \"inherit\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Beispieltext",
    "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_active": true,
    "inherit": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Beispieltext',
            'body_html' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
            'is_active' => true,
            'inherit' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Beispieltext",
    "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_active": true,
    "inherit": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The contract template has been saved.",
    "data": {
        "template": {
            "name": "Überlassungsvertrag",
            "body_html": "<h2>Arbeitskraft</h2>\n<p>{worker_name}, geboren am {worker_birth_date}, {worker_address}</p>\n<h2>Beschäftiger</h2>\n<p>{organization_name}, {organization_address}</p>\n<h2>Überlassung</h2>\n<p>Tätigkeit: {job_title}<br>Einsatzort: {location}<br>Zeitraum: {start_date} – {end_date}<br>Entgelt (brutto/Std.): {hourly_rate}<br>Kollektivvertrag: {collective_agreement}</p>\n<p>Für die Dauer der Überlassung gelten gemäß § 10 AÜG die im Beschäftigerbetrieb für vergleichbare Arbeitnehmer geltenden wesentlichen Arbeits- und Beschäftigungsbedingungen. Rechtsgrundlage: {legal_basis}.</p>",
            "version": 0,
            "is_active": false,
            "is_customized": false
        }
    }
}
 

Request      

PUT api/v1/org/jobs/{jobId}/contract-template

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

name   string  optional    

Example: Beispieltext

body_html   string     

Example: Bitte um Rueckmeldung zur naechsten Schicht.

is_active   boolean  optional    

Example: true

inherit   boolean  optional    

Example: true

Shortlist Applicant

requires authentication

Shortlist an applicant for consideration.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shortlist" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shortlist"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shortlist';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shortlist');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "messages.applicant_shortlisted",
    "data": {
        "application": {
            "id": "019fd2b8-76c6-72bb-a2d9-98ed4934fb02",
            "status": "shortlisted",
            "shortlisted_at": "2026-08-05 16:18"
        }
    }
}
 

Request      

POST api/v1/org/jobs/{jobId}/applicants/{applicationId}/shortlist

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

applicationId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Select Applicant

requires authentication

Select an applicant for the job and create their shifts.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/select" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/select"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/select';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/select');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The applicant has been selected and assigned to shifts.",
    "data": {
        "application": {
            "id": "019fd2b8-76c6-72bb-a2d9-98ed4934fb02",
            "status": "selected",
            "selected_at": "2026-08-05 16:18"
        },
        "shifts_created": 1,
        "job_vacancies": {
            "total": 3,
            "filled": 0,
            "available": 3
        }
    }
}
 

Request      

POST api/v1/org/jobs/{jobId}/applicants/{applicationId}/select

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

applicationId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Add To Waitlist

requires authentication

Add an applicant to the waitlist (standby).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/waitlist" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"position\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/waitlist"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "position": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/waitlist';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'position' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/waitlist');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "position": 1
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "messages.applicant_added_to_waitlist",
    "data": {
        "application": {
            "id": "019fd2b8-7cd7-7331-a92c-cf1a3939282f",
            "status": "standby",
            "waitlist_position": 1,
            "added_to_waitlist_at": "2026-08-05 16:18"
        },
        "waitlist_summary": {
            "total": 1,
            "max_size": 5,
            "positions": [
                {
                    "position": 1,
                    "application_id": "019fd2b8-7cd7-7331-a92c-cf1a3939282f",
                    "added_at": "2026-08-05 16:18"
                }
            ]
        }
    }
}
 

Request      

POST api/v1/org/jobs/{jobId}/applicants/{applicationId}/waitlist

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

applicationId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

position   integer  optional    

Example: 1

Reject Applicant

requires authentication

Reject an applicant for the job.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason_code\": \"position_filled\",
    \"internal_note\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason_code": "position_filled",
    "internal_note": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason_code' => 'position_filled',
            'internal_note' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason_code": "position_filled",
    "internal_note": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The applicant has been rejected.",
    "data": {
        "application": {
            "id": "019fd2b8-7cd7-7331-a92c-cf1a3939282f",
            "status": "rejected",
            "rejected_at": "2026-08-05 16:18"
        }
    }
}
 

Request      

POST api/v1/org/jobs/{jobId}/applicants/{applicationId}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

applicationId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason_code   string     

Allowed: position_filled, qualifications_not_met, insufficient_experience, availability_mismatch, language_requirement, better_candidate_selected, other. Example: position_filled

internal_note   string  optional    

Example: Beispieltext

Deassign Shift

requires authentication

De-assign a worker from one of their chosen shifts.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/deassign" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/deassign"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/deassign';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/019f9939-c4c3-70fb-a54e-5ebf63db36d2/applicants/019f9939-c4c3-70fb-a54e-5ebf63db36d2/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/deassign');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/jobs/{jobId}/applicants/{applicationId}/shifts/{shiftId}/deassign

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

applicationId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Applicants

Get a single applicant's profile.

requires authentication

Per Austrian AÜG (Arbeitskräfteüberlassungsgesetz), client organizations must be able to verify worker qualifications before assignment — but not their identity, which stays masked until the company selects them.

Passing reveal=true unmasks it early. That requires the audit.view permission and writes an OrgPiiReveal audit entry before the data is returned, so the record is made where the disclosure actually happens rather than wherever a client chooses to report it.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/applicants/architecto?reveal=1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/applicants/architecto"
);

const params = {
    "reveal": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/applicants/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'reveal' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/applicants/architecto')
      .replace(queryParameters: {
        'reveal': '1',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "id": "019fd2b8-76c6-72bb-a2d9-98ed4934fb02",
        "status": "selected",
        "status_label": "Ausgewählt",
        "applied_at": "2026-08-05 16:18",
        "has_scheduling_conflict": false,
        "acknowledged_conflict": false,
        "shortlisted_at": "2026-08-05 16:18",
        "selected_at": "2026-08-05 16:18",
        "rejected_at": null,
        "notes": null,
        "chosen_shifts": [
            {
                "id": "019fd2b8-756e-70aa-a7ad-01157af79df0",
                "shift_date": "2026-08-12",
                "start_time": "08:00:00",
                "end_time": "16:00:00"
            }
        ],
        "cover_letter": null,
        "availability": null,
        "expected_hourly_rate": null,
        "application_documents": [],
        "user": {
            "id": "019fd2b8-7352-72bc-9514-ac7713e5c225",
            "display_name": "Fae Green",
            "first_name": "Fae",
            "last_name": "Green",
            "email": "ruthie17@example.com",
            "phone": null,
            "avatar_url": null
        },
        "profile": {
            "nationality": "AT",
            "city": "North Asa",
            "gender": "not_specified",
            "date_of_birth": "2007-02-21",
            "profile_completion_percentage": 86,
            "avg_rating": 0,
            "reliability_score": 100,
            "punctuality_percentage": null,
            "hours_worked": 0,
            "total_shifts_completed": 0,
            "no_shows_count": 0,
            "bio": null,
            "education": null,
            "skills": [],
            "languages": [],
            "certifications": [],
            "reviews": [],
            "documents": []
        }
    }
}
 

Example response (403, Not permitted to reveal):


{
    "status": "FORBIDDEN",
    "message": "You are not permitted to reveal applicant identities."
}
 

Request      

GET api/v1/org/jobs/{jobId}/applicants/{applicationId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Example: architecto

applicationId   string     

Example: architecto

Query Parameters

reveal   boolean  optional    

Unmask the applicant's identity. Requires audit.view; audited. Example: true

Shifts

Get Shift Detail

requires authentication

Returns a single shift slot with its worker assignments and each assignment's live check-in / clock state — backs the portal's live shift view page.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "shift": {
            "id": "019fd2b8-28d5-7103-b951-9f6ff1d94556",
            "job": {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant"
            },
            "shift_date": "2026-08-05",
            "start_time": "08:00:00",
            "end_time": "16:00:00",
            "break_minutes": 30,
            "workers_needed": 2,
            "notes": null,
            "status": "scheduled",
            "status_label": "Geplant",
            "shift_manager": null,
            "counts": {
                "needed": 2,
                "awaiting_signature": 0,
                "signed": 0,
                "checked_in": 0,
                "completed": 0,
                "no_show": 0
            },
            "assignments": []
        }
    }
}
 

Request      

GET api/v1/org/shifts/{shiftId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Get Shift Contract Template

requires authentication

A shift's contract template for the shift editor: the shift-level override when one exists ("customized"), otherwise the effective template inherited from the job override (or the organisation default, or the built-in body) — plus the merge fields.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "template": {
            "name": "Überlassungsvertrag",
            "body_html": "<h2>Arbeitskraft</h2>\n<p>{worker_name}, geboren am {worker_birth_date}, {worker_address}</p>\n<h2>Beschäftiger</h2>\n<p>{organization_name}, {organization_address}</p>\n<h2>Überlassung</h2>\n<p>Tätigkeit: {job_title}<br>Einsatzort: {location}<br>Zeitraum: {start_date} – {end_date}<br>Entgelt (brutto/Std.): {hourly_rate}<br>Kollektivvertrag: {collective_agreement}</p>\n<p>Für die Dauer der Überlassung gelten gemäß § 10 AÜG die im Beschäftigerbetrieb für vergleichbare Arbeitnehmer geltenden wesentlichen Arbeits- und Beschäftigungsbedingungen. Rechtsgrundlage: {legal_basis}.</p>",
            "version": 0,
            "is_active": false,
            "is_customized": false
        },
        "merge_fields": [
            "contract_number",
            "worker_name",
            "worker_birth_date",
            "worker_nationality",
            "worker_address",
            "organization_name",
            "organization_address",
            "job_title",
            "job_description",
            "location",
            "start_date",
            "end_date",
            "hourly_rate",
            "collective_agreement",
            "legal_basis"
        ]
    }
}
 

Request      

GET api/v1/org/shifts/{shiftId}/contract-template

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Update Shift Contract Template

requires authentication

Save (or reset) a shift's contract template override. inherit: true deletes the shift-level row so the shift falls back to its job's template (or the organisation default); otherwise the body is upserted, seeded from the resolved job-level body the first time a shift is customised.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Beispieltext\",
    \"body_html\": \"Bitte um Rueckmeldung zur naechsten Schicht.\",
    \"is_active\": true,
    \"inherit\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Beispieltext",
    "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_active": true,
    "inherit": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Beispieltext',
            'body_html' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
            'is_active' => true,
            'inherit' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/contract-template');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Beispieltext",
    "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_active": true,
    "inherit": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The contract template has been saved.",
    "data": {
        "template": {
            "name": "Überlassungsvertrag",
            "body_html": "<h2>Arbeitskraft</h2>\n<p>{worker_name}, geboren am {worker_birth_date}, {worker_address}</p>\n<h2>Beschäftiger</h2>\n<p>{organization_name}, {organization_address}</p>\n<h2>Überlassung</h2>\n<p>Tätigkeit: {job_title}<br>Einsatzort: {location}<br>Zeitraum: {start_date} – {end_date}<br>Entgelt (brutto/Std.): {hourly_rate}<br>Kollektivvertrag: {collective_agreement}</p>\n<p>Für die Dauer der Überlassung gelten gemäß § 10 AÜG die im Beschäftigerbetrieb für vergleichbare Arbeitnehmer geltenden wesentlichen Arbeits- und Beschäftigungsbedingungen. Rechtsgrundlage: {legal_basis}.</p>",
            "version": 0,
            "is_active": false,
            "is_customized": false
        }
    }
}
 

Request      

PUT api/v1/org/shifts/{shiftId}/contract-template

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

name   string  optional    

Example: Beispieltext

body_html   string     

Example: Bitte um Rueckmeldung zur naechsten Schicht.

is_active   boolean  optional    

Example: true

inherit   boolean  optional    

Example: true

Scan Check In Qr

requires authentication

Scan an employee's QR code to record check-in/check-out.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"qr_payload\": \"Beispieltext\",
    \"lat\": 1,
    \"lng\": 1,
    \"signature\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "qr_payload": "Beispieltext",
    "lat": 1,
    "lng": 1,
    "signature": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'qr_payload' => 'Beispieltext',
            'lat' => 1,
            'lng' => 1,
            'signature' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "qr_payload": "Beispieltext",
    "lat": 1,
    "lng": 1,
    "signature": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/scan-qr

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

qr_payload   string     

Example: Beispieltext

lat   integer  optional    

Example: 1

lng   integer  optional    

Example: 1

signature   string  optional    

Example: Beispieltext

Cancel Shift By Organisation

requires authentication

Organization cancels a shift.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/cancel');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/{shiftId}/cancel

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

Notify Shift Extension

requires authentication

Allows an organisation to extend an active shift — updating the end time and notifying the assigned employee via push notification.

The extension is only allowed when the shift is currently in_progress. The organisation must provide a reason and the new end time must be later than the current scheduled end time.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/extend" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"extended_end_time\": \"20:00\",
    \"reason\": \"Production running late, need 2 more hours.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/extend"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "extended_end_time": "20:00",
    "reason": "Production running late, need 2 more hours."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/extend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'extended_end_time' => '20:00',
            'reason' => 'Production running late, need 2 more hours.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/extend');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "extended_end_time": "20:00",
    "reason": "Production running late, need 2 more hours."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "message": "Schichtverlängerung wurde gemeldet.",
        "extended_end_time": "20:00"
    }
}
 

Example response (400):


{
    "status": "ERROR",
    "message": "Die Schicht ist nicht aktiv."
}
 

Example response (403):


{
    "status": "ERROR",
    "message": "Diese Schicht gehört nicht zu Ihrem Unternehmen."
}
 

Example response (404):


{
    "status": "ERROR",
    "message": "Schicht nicht gefunden."
}
 

Example response (422):


{
    "status": "ERROR",
    "message": "The extended end time field is required."
}
 

Request      

POST api/v1/org/shifts/{shiftId}/extend

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

UUID of the shift. Example: 9c3f5f3d-0123-4abc-b456-426614174000

Body Parameters

extended_end_time   string     

New end time in HH:MM format (must be after current end). Example: 20:00

reason   string     

Reason for the extension (max 500 chars). Example: Production running late, need 2 more hours.

Rate Employee

requires authentication

Organization submits a rating for an employee after shift completion.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"overall_score\": 1,
    \"punctuality_score\": 1,
    \"professionalism_score\": 1,
    \"communication_score\": 1,
    \"work_quality_score\": 1,
    \"comment\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "overall_score": 1,
    "punctuality_score": 1,
    "professionalism_score": 1,
    "communication_score": 1,
    "work_quality_score": 1,
    "comment": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'overall_score' => 1,
            'punctuality_score' => 1,
            'professionalism_score' => 1,
            'communication_score' => 1,
            'work_quality_score' => 1,
            'comment' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/019f9939-c4c3-70fb-a54e-5ebf63db36d2/rate');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "overall_score": 1,
    "punctuality_score": 1,
    "professionalism_score": 1,
    "communication_score": 1,
    "work_quality_score": 1,
    "comment": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your rating has been submitted successfully.",
    "data": {
        "rating": {
            "id": "019fd2b8-45d9-707b-89cf-d4a026834fe3",
            "overall_score": 1,
            "created_at": "2026-08-05 16:18"
        }
    }
}
 

Request      

POST api/v1/org/shifts/{shiftId}/rate

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

overall_score   integer     

Example: 1

punctuality_score   integer  optional    

Example: 1

professionalism_score   integer  optional    

Example: 1

communication_score   integer  optional    

Example: 1

work_quality_score   integer  optional    

Example: 1

comment   string  optional    

Example: Beispieltext

List Shift Requests

requires authentication

The company's inbox of worker requests. Pending ones come first regardless of sort, because they are the only ones that need an action — and until they are decided the shift is not settled and the worker is not paid.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/requests?status=pending&type=overtime&shift_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2&per_page=25" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"declined\",
    \"type\": \"manual_check_in\",
    \"shift_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"per_page\": 7
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/requests"
);

const params = {
    "status": "pending",
    "type": "overtime",
    "shift_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "declined",
    "type": "manual_check_in",
    "shift_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "per_page": 7
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'pending',
            'type' => 'overtime',
            'shift_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'per_page' => '25',
        ],
        'json' => [
            'status' => 'declined',
            'type' => 'manual_check_in',
            'shift_id' => '6ff8f7f6-1eb3-3525-be4a-3932c805afed',
            'per_page' => 7,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/requests')
      .replace(queryParameters: {
        'status': 'pending',
        'type': 'overtime',
        'shift_id': '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        'per_page': '25',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "status": "declined",
    "type": "manual_check_in",
    "shift_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "per_page": 7
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "requests": [],
        "pending_count": 0,
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 7,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/org/requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Allowed: pending, approved, declined, withdrawn. Example: pending

type   string  optional    

Allowed: overtime, early_end, hours_correction, manual_check_in, absence_excuse, workplace_change. Example: overtime

shift_id   string  optional    

Only requests against this assignment. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

per_page   integer  optional    

Items per page (max 100). Example: 25

Body Parameters

status   string  optional    

Example: declined

Must be one of:
  • pending
  • approved
  • declined
  • withdrawn
type   string  optional    

Example: manual_check_in

Must be one of:
  • overtime
  • early_end
  • hours_correction
  • manual_check_in
  • absence_excuse
  • workplace_change
shift_id   string  optional    

Must be a valid UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 7

Approve or Decline a Shift Request

requires authentication

The company's answer to a worker. Approving overtime raises what the shift pays; declining leaves it at the capped amount. Either way the decision is stamped with whoever made it — the organisation account or the member — and written to the audit trail, because it changes what someone is paid.

A decline must carry a reason. The worker sees it.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/decide" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"approve\": true,
    \"note\": \"Overtime was not authorised on site.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/decide"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "approve": true,
    "note": "Overtime was not authorised on site."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/decide';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'approve' => true,
            'note' => 'Overtime was not authorised on site.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/decide');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "approve": true,
    "note": "Overtime was not authorised on site."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (409, Already decided):


{
    "status": "INVALID_OPERATION",
    "message": "This request has already been decided."
}
 

Request      

POST api/v1/org/requests/{requestId}/decide

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

requestId   string     

The request being decided. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

approve   boolean     

Whether the request is granted. Example: true

note   string  optional    

The reason. Required when declining. Example: Overtime was not authorised on site.

Open a Conversation About a Shift Request

requires authentication

Starts (or returns) the thread between the company and the worker about one specific request — for when a manager needs to ask something before deciding, rather than approving or declining blind.

Scoped to the request, not the shift, so the exchange stays attached to the decision it belongs to. Calling this twice returns the same thread.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/conversation" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/conversation"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/conversation';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/requests/019f9939-c4c3-70fb-a54e-5ebf63db36d2/conversation');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Thread ready):


{
    "status": "SUCCESS",
    "data": {
        "conversation_id": "019f...",
        "shift_request_id": "019f...",
        "subject": "Überstunden"
    }
}
 

Request      

POST api/v1/org/requests/{requestId}/conversation

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

requestId   string     

The request to discuss. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Notifications

List Notifications

requires authentication

Paginated list of notifications for the authenticated organisation. Includes an unread_count in meta for the inbox badge.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/notifications?page=16&per_page=16&unread_only=" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/notifications"
);

const params = {
    "page": "16",
    "per_page": "16",
    "unread_only": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/notifications';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'page' => '16',
            'per_page' => '16',
            'unread_only' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/notifications')
      .replace(queryParameters: {
        'page': '16',
        'per_page': '16',
        'unread_only': '',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Notifications retrieved successfully.",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "09377940-8cb9-4640-a522-b88869dfb7fe",
        "timestamp": "2026-08-05T16:18:38.542194Z",
        "current_page": 16,
        "per_page": 16,
        "total": 0,
        "last_page": 1,
        "unread_count": 0
    }
}
 

Request      

GET api/v1/org/notifications

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

page   integer  optional    

Defaults to 1. Example: 16

per_page   integer  optional    

Defaults to 20 (max 100). Example: 16

unread_only   boolean  optional    

nullable When true, returns only unread notifications. Example: false

Get Unread Notifications Count

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/notifications/unread-count" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/notifications/unread-count"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/notifications/unread-count';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/notifications/unread-count');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "count": 0
    },
    "errors": null,
    "meta": {
        "request_id": "a0972d73-64ed-4c2d-a149-88962625ec88",
        "timestamp": "2026-08-05T16:18:38.548222Z"
    }
}
 

Request      

GET api/v1/org/notifications/unread-count

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Mark Notification As Read

requires authentication

Marks a single notification as read for the authenticated organisation.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/notifications/architecto/read" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/notifications/architecto/read"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/notifications/architecto/read';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/notifications/architecto/read');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Notification marked as read.",
    "data": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "read_at": "2026-04-02T12:00:00+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Notification not found.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/notifications/{notification}/read

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

notification   string     

The notification. Example: architecto

id   string     

The notification UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Mark All Notifications As Read

requires authentication

Marks every unread notification of the authenticated organisation as read in a single call.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/notifications/read-all" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/notifications/read-all"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/notifications/read-all';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/notifications/read-all');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "All notifications marked as read.",
    "data": {
        "marked": true
    },
    "errors": null,
    "meta": {
        "request_id": "d6f09336-8a5c-476c-880f-8abb84800194",
        "timestamp": "2026-08-05T16:18:38.554649Z"
    }
}
 

Request      

POST api/v1/org/notifications/read-all

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Notification Preferences

requires authentication

Returns per-category notification preferences for the authenticated organisation. Defaults to email enabled / push disabled for every category.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/notification-preferences" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/notification-preferences"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/notification-preferences';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/notification-preferences');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "category": "jobs",
            "email": true,
            "push": false
        },
        {
            "category": "finance",
            "email": true,
            "push": false
        },
        {
            "category": "communication",
            "email": true,
            "push": false
        },
        {
            "category": "security",
            "email": true,
            "push": false
        },
        {
            "category": "system",
            "email": true,
            "push": false
        },
        {
            "category": "marketing",
            "email": true,
            "push": false
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "062e170a-40d4-47f4-a976-a64ae4b9bad4",
        "timestamp": "2026-08-05T16:18:38.560328Z"
    }
}
 

Request      

GET api/v1/org/notification-preferences

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Notification Preferences

requires authentication

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/notification-preferences" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"category\": \"finance\",
    \"channel\": \"email\",
    \"enabled\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/notification-preferences"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "category": "finance",
    "channel": "email",
    "enabled": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/notification-preferences';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'category' => 'finance',
            'channel' => 'email',
            'enabled' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/notification-preferences');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "category": "finance",
    "channel": "email",
    "enabled": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Notification preference updated.",
    "data": {
        "category": "finance",
        "email": true,
        "push": false
    },
    "errors": null,
    "meta": {
        "request_id": "7f992b80-e97b-48f0-a60d-8d3da4d4684e",
        "timestamp": "2026-08-05T16:18:38.567378Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "category",
            "message": "The selected category is invalid."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

PUT api/v1/org/notification-preferences

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

category   string     

Notification category (jobs, finance, communication, security, system, marketing). Example: finance

channel   string     

Allowed: email, push. Example: email

enabled   boolean     

true to enable, false to disable. Example: true

Auth

Forgot Password

Sends a 4-digit password reset code to the given email address.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If that email address is registered, a password reset code has been sent.",
    "data": {
        "email": "opal56@example.net",
        "code": "448891"
    },
    "errors": null,
    "meta": {
        "request_id": "04e13c3e-6945-4f40-bb63-a330434430e3",
        "timestamp": "2026-08-05T16:18:54.164086Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "guard",
            "message": "The guard field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/forgot-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to send reset code to. Example: boris@example.com

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Verify Reset Code

Validates the 4-digit password reset code for the given email and guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/verify" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"code\": \"123456\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/verify"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "code": "123456",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'code' => '123456',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/verify');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "code": "123456",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Reset code verified successfully.",
    "data": {
        "reset_token": "c97701d0-c4de-4755-b3c4-f7d1a803efc5"
    },
    "errors": null,
    "meta": {
        "request_id": "67df2724-9771-46b5-8d4d-71f98573d3e5",
        "timestamp": "2026-08-05T16:18:54.166696Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "RESET_CODE_INVALID",
    "message": "Invalid reset code.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Invalid reset code."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (410, Code Expired):


{
    "status": "RESET_CODE_EXPIRED",
    "message": "Reset code has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Reset code has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The code field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Too Many Attempts):


{
    "status": "RESET_ATTEMPTS_EXHAUSTED",
    "message": "Too many failed attempts. Please request a new code.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Too many failed attempts. Please request a new code."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/forgot-password/verify

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address for password reset. Example: boris@example.com

code   string     

6-digit reset code. Example: 123456

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Resend Reset Code

Re-sends a 4-digit password reset code for the given email and guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/resend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/forgot-password/resend');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If that email address is registered, a new password reset code has been sent.",
    "data": {
        "code": "443677"
    },
    "errors": null,
    "meta": {
        "request_id": "a265e26e-388c-4782-b9ce-bc4722cc5958",
        "timestamp": "2026-08-05T16:18:58.095803Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "guard",
            "message": "The guard field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/forgot-password/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to resend reset code to. Example: boris@example.com

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Reset Password

Resets the password for any actor type and revokes all existing tokens.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/reset-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"reset_token\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",
    \"password\": \"NewPassword123\",
    \"password_confirmation\": \"NewPassword123\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/reset-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "reset_token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "password": "NewPassword123",
    "password_confirmation": "NewPassword123",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/reset-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'reset_token' => 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
            'password' => 'NewPassword123',
            'password_confirmation' => 'NewPassword123',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/reset-password');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "reset_token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "password": "NewPassword123",
    "password_confirmation": "NewPassword123",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password has been reset successfully.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "85c34424-2c5f-4d2e-aa97-5edf63d5d429",
        "timestamp": "2026-08-05T16:18:54.626775Z"
    }
}
 

Example response (403, Email Not Verified):


{
    "status": "RESET_NOT_VERIFIED",
    "message": "Email not verified. Complete the verification step first.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Email not verified. Complete the verification step first."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (404, Account Not Found):


{
    "status": "ACCOUNT_NOT_FOUND",
    "message": "Account not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Account not found."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "The password must be at least 8 characters."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Password Reuse):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "You cannot reuse your last 3 passwords."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/reset-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address for account. Example: boris@example.com

reset_token   string     

UUID from verify-reset-code step. Example: a1b2c3d4-e5f6-7890-abcd-ef1234567890

password   string     

New password (min 8, must be confirmed). Example: NewPassword123

password_confirmation   string     

Password confirmation. Example: NewPassword123

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Two Factor Challenge

requires authentication

Completes the two-factor authentication challenge for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/challenge" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/challenge"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/challenge';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/challenge');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication verified successfully.",
    "data": {
        "token": "42|wMrUbv7gI4CRWxcRbQIldttPzbyXBl2z181bI8Yt86612731",
        "refresh_token": "43|VPtqTDVPb3gY2MF4mIkms7vJcLWgUA47iwIxRI8e34e4f629",
        "expires_in": 900,
        "user": {
            "id": "019fd2b8-b86e-738d-8bfa-a29262a83558",
            "email": "hansen.mohammad@example.org"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "0ff1a162-0832-4ffa-9700-d0f30d00a580",
        "timestamp": "2026-08-05T16:19:09.319799Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, 2FA Not Configured):


{
    "status": "TWO_FACTOR_NOT_CONFIGURED",
    "message": "Two-factor authentication is not configured for this account.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not configured for this account."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/two-factor/challenge

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

The OTP or TOTP code. Nullable if recovery_code is provided. Example: 123456

recovery_code   string  optional    

The recovery code. Nullable if code is provided. Example: ABCD-1234-EFGH

Logout

requires authentication

Revokes the currently active Sanctum token for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/logout" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/logout"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/logout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/logout');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (204, Success):

Empty response
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/logout

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Enable Two Factor

requires authentication

Initiates the 2FA setup flow for the authenticated user.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/enable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"method\": \"totp\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/enable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "method": "totp"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/enable';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'method' => 'totp',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/enable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "method": "totp"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Email):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication initiated.",
    "data": {
        "method": "email"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Scan the QR code with your authenticator app, then confirm with the generated code.",
    "data": {
        "method": "totp",
        "secret": "6RL4HDCP2F4CDU27SRQOWOS454GFGIL6",
        "qr_uri": "otpauth://totp/Flexxer%20Backend:019fd2b8-b36f-7307-92fb-5d28e58ef9f2?secret=6RL4HDCP2F4CDU27SRQOWOS454GFGIL6&issuer=Flexxer%20Backend&algorithm=SHA1&digits=6&period=30"
    },
    "errors": null,
    "meta": {
        "request_id": "e9e9b449-d901-409b-a9b4-fd071002aa0e",
        "timestamp": "2026-08-05T16:19:07.791377Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "method",
            "message": "The selected method is invalid."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/two-factor/enable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

method   string     

The 2FA method to enable. Example: totp

Must be one of:
  • email
  • totp. Allowed: email
  • totp

Confirm Two Factor

requires authentication

Confirms and activates 2FA after the user has verified their code.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/confirm" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/confirm');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication has been enabled. Save your recovery codes in a safe place.",
    "data": {
        "recovery_codes": [
            "DTPLO-U2SCF",
            "MRWS1-IKBUY",
            "MVM4F-Z42ES",
            "AUSXU-FTDPK",
            "U7WHX-TIU99",
            "12AWU-NZVH6",
            "ZY5XK-4WOKJ",
            "47MAL-ZSDLU"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "315aafcb-764e-4f38-9faa-b9c4f45e3461",
        "timestamp": "2026-08-05T16:19:07.807225Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, No Pending Setup):


{
    "status": "TWO_FACTOR_NO_PENDING_SETUP",
    "message": "No pending two-factor setup found. Please restart the setup process.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "No pending two-factor setup found. Please restart the setup process."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The code field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/two-factor/confirm

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string     

The OTP or TOTP code to verify. Example: 123456

Request Disable Two Factor Code

requires authentication

Dispatches a 6-digit OTP to the user's account email so they can confirm a 2FA-disable request. No-op for TOTP accounts (the code comes from the authenticator app directly) and for accounts without an enabled 2FA setting — both return 200 so the client's happy path is uniform.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable/request-code" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable/request-code"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable/request-code';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable/request-code');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, TOTP — use authenticator):


{
    "status": "SUCCESS",
    "message": "Enter the current code from your authenticator app.",
    "data": {
        "method": "totp"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Enter the current code from your authenticator app.",
    "data": {
        "method": "totp"
    },
    "errors": null,
    "meta": {
        "request_id": "0542b19f-0c0a-4bf3-93a4-d90bbac46f63",
        "timestamp": "2026-08-05T16:18:37.138042Z"
    }
}
 

Example response (409, Not Enabled):


{
    "status": "TWO_FACTOR_NOT_ENABLED",
    "message": "Two-factor authentication is not currently enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not currently enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/mobile/auth/two-factor/disable/request-code

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Disable Two Factor

requires authentication

Disables 2FA for the authenticated user after verifying a current challenge code — the same proof the user provides at login time.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/auth/two-factor/disable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.delete(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication has been disabled.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "255f0d6e-8c58-4e47-84a4-c47dd821febf",
        "timestamp": "2026-08-05T16:19:07.817111Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (409, Not Enabled):


{
    "status": "TWO_FACTOR_NOT_ENABLED",
    "message": "Two-factor authentication is not currently enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not currently enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/mobile/auth/two-factor/disable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

6-digit TOTP or email OTP. Required unless recovery_code is provided. Example: 123456

recovery_code   string  optional    

Recovery code in place of code. Example: ABCD-1234-EFGH

Forgot Password

Sends a 4-digit password reset code to the given email address.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If that email address is registered, a password reset code has been sent.",
    "data": {
        "email": "opal56@example.net",
        "code": "448891"
    },
    "errors": null,
    "meta": {
        "request_id": "04e13c3e-6945-4f40-bb63-a330434430e3",
        "timestamp": "2026-08-05T16:18:54.164086Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "guard",
            "message": "The guard field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/forgot-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to send reset code to. Example: boris@example.com

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Verify Reset Code

Validates the 4-digit password reset code for the given email and guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/verify" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"code\": \"123456\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/verify"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "code": "123456",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'code' => '123456',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/verify');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "code": "123456",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Reset code verified successfully.",
    "data": {
        "reset_token": "c97701d0-c4de-4755-b3c4-f7d1a803efc5"
    },
    "errors": null,
    "meta": {
        "request_id": "67df2724-9771-46b5-8d4d-71f98573d3e5",
        "timestamp": "2026-08-05T16:18:54.166696Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "RESET_CODE_INVALID",
    "message": "Invalid reset code.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Invalid reset code."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (410, Code Expired):


{
    "status": "RESET_CODE_EXPIRED",
    "message": "Reset code has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Reset code has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The code field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Too Many Attempts):


{
    "status": "RESET_ATTEMPTS_EXHAUSTED",
    "message": "Too many failed attempts. Please request a new code.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Too many failed attempts. Please request a new code."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/forgot-password/verify

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address for password reset. Example: boris@example.com

code   string     

6-digit reset code. Example: 123456

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Resend Reset Code

Re-sends a 4-digit password reset code for the given email and guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/resend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/forgot-password/resend');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If that email address is registered, a new password reset code has been sent.",
    "data": {
        "code": "443677"
    },
    "errors": null,
    "meta": {
        "request_id": "a265e26e-388c-4782-b9ce-bc4722cc5958",
        "timestamp": "2026-08-05T16:18:58.095803Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "guard",
            "message": "The guard field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/forgot-password/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to resend reset code to. Example: boris@example.com

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Reset Password

Resets the password for any actor type and revokes all existing tokens.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/reset-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"reset_token\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",
    \"password\": \"NewPassword123\",
    \"password_confirmation\": \"NewPassword123\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/reset-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "reset_token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "password": "NewPassword123",
    "password_confirmation": "NewPassword123",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/reset-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'reset_token' => 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
            'password' => 'NewPassword123',
            'password_confirmation' => 'NewPassword123',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/reset-password');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "reset_token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "password": "NewPassword123",
    "password_confirmation": "NewPassword123",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password has been reset successfully.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "85c34424-2c5f-4d2e-aa97-5edf63d5d429",
        "timestamp": "2026-08-05T16:18:54.626775Z"
    }
}
 

Example response (403, Email Not Verified):


{
    "status": "RESET_NOT_VERIFIED",
    "message": "Email not verified. Complete the verification step first.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Email not verified. Complete the verification step first."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (404, Account Not Found):


{
    "status": "ACCOUNT_NOT_FOUND",
    "message": "Account not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Account not found."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "The password must be at least 8 characters."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Password Reuse):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "You cannot reuse your last 3 passwords."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/reset-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address for account. Example: boris@example.com

reset_token   string     

UUID from verify-reset-code step. Example: a1b2c3d4-e5f6-7890-abcd-ef1234567890

password   string     

New password (min 8, must be confirmed). Example: NewPassword123

password_confirmation   string     

Password confirmation. Example: NewPassword123

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Two Factor Challenge

requires authentication

Completes the two-factor authentication challenge for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/challenge" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/challenge"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/challenge';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/challenge');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication verified successfully.",
    "data": {
        "token": "42|wMrUbv7gI4CRWxcRbQIldttPzbyXBl2z181bI8Yt86612731",
        "refresh_token": "43|VPtqTDVPb3gY2MF4mIkms7vJcLWgUA47iwIxRI8e34e4f629",
        "expires_in": 900,
        "user": {
            "id": "019fd2b8-b86e-738d-8bfa-a29262a83558",
            "email": "hansen.mohammad@example.org"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "0ff1a162-0832-4ffa-9700-d0f30d00a580",
        "timestamp": "2026-08-05T16:19:09.319799Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, 2FA Not Configured):


{
    "status": "TWO_FACTOR_NOT_CONFIGURED",
    "message": "Two-factor authentication is not configured for this account.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not configured for this account."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/two-factor/challenge

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

The OTP or TOTP code. Nullable if recovery_code is provided. Example: 123456

recovery_code   string  optional    

The recovery code. Nullable if code is provided. Example: ABCD-1234-EFGH

Two Factor Challenge

requires authentication

Completes the two-factor authentication challenge for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/member/two-factor/challenge" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/member/two-factor/challenge"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/member/two-factor/challenge';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/member/two-factor/challenge');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication verified successfully.",
    "data": {
        "token": "42|wMrUbv7gI4CRWxcRbQIldttPzbyXBl2z181bI8Yt86612731",
        "refresh_token": "43|VPtqTDVPb3gY2MF4mIkms7vJcLWgUA47iwIxRI8e34e4f629",
        "expires_in": 900,
        "user": {
            "id": "019fd2b8-b86e-738d-8bfa-a29262a83558",
            "email": "hansen.mohammad@example.org"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "0ff1a162-0832-4ffa-9700-d0f30d00a580",
        "timestamp": "2026-08-05T16:19:09.319799Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, 2FA Not Configured):


{
    "status": "TWO_FACTOR_NOT_CONFIGURED",
    "message": "Two-factor authentication is not configured for this account.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not configured for this account."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/member/two-factor/challenge

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

The OTP or TOTP code. Nullable if recovery_code is provided. Example: 123456

recovery_code   string  optional    

The recovery code. Nullable if code is provided. Example: ABCD-1234-EFGH

Get Current Organization / Member

requires authentication

Returns the authenticated actor's profile alongside an ACL manifest.

Two actor types share this route (both pass the guard:organization middleware):

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/auth/me" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/me"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/me');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, OrganizationMember):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "profile": {
            "id": "uuid",
            "name": "Jane Doe",
            "email": "jane@example.com",
            "role": "member",
            "organization_id": "org-uuid",
            "organization_name": "Acme e.V.",
            "is_primary_admin": false
        },
        "acl": {
            "scope": "org-member",
            "presets": [
                "viewer"
            ],
            "permissions": [
                "profile.view",
                "team.view"
            ],
            "conditional": {},
            "computed_at": "2026-05-12T10:00:00+00:00"
        }
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "profile": {
            "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "name": "Caritas",
            "email": "caritas-portal@flexxr.eu.cc",
            "status": "active",
            "is_primary_admin": true
        },
        "acl": {
            "scope": "org-member",
            "presets": [
                "principal"
            ],
            "permissions": [
                "applicants.accept",
                "applicants.reject",
                "applicants.view",
                "assignments.manage",
                "assignments.view",
                "attendance.approve-hours",
                "attendance.report-noshow",
                "attendance.view",
                "audit.view",
                "gdpr.request",
                "invoices.download",
                "invoices.view",
                "jobs.configure-assignment",
                "jobs.create",
                "jobs.delete",
                "jobs.edit",
                "jobs.publish",
                "jobs.view",
                "messages.access",
                "notifications.preferences.manage",
                "payments.manage",
                "payments.view",
                "profile.edit",
                "profile.view",
                "ratings.create",
                "ratings.view",
                "reports.export",
                "reports.view",
                "requests.decide",
                "requests.view",
                "shifts.adjust",
                "shifts.cancel",
                "shifts.check-in",
                "shifts.dispute-time",
                "shifts.generate-qr",
                "shifts.report-signature-refusal",
                "shifts.view",
                "support.create-ticket",
                "support.view-tickets",
                "team.invite",
                "team.manage-roles",
                "team.remove",
                "team.view"
            ],
            "conditional": {},
            "computed_at": "2026-08-05T16:18:37+00:00"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "e4ea4c8e-c454-4449-891f-5f615816a929",
        "timestamp": "2026-08-05T16:18:37.721087Z"
    }
}
 

Request      

GET api/v1/org/auth/me

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Logout

requires authentication

Revokes the currently active Sanctum token for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/logout" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/logout"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/logout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/logout');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (204, Success):

Empty response
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/logout

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Enable Two Factor

requires authentication

Initiates the 2FA setup flow for the authenticated user.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/enable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"method\": \"totp\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/enable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "method": "totp"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/enable';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'method' => 'totp',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/enable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "method": "totp"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Email):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication initiated.",
    "data": {
        "method": "email"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Scan the QR code with your authenticator app, then confirm with the generated code.",
    "data": {
        "method": "totp",
        "secret": "6RL4HDCP2F4CDU27SRQOWOS454GFGIL6",
        "qr_uri": "otpauth://totp/Flexxer%20Backend:019fd2b8-b36f-7307-92fb-5d28e58ef9f2?secret=6RL4HDCP2F4CDU27SRQOWOS454GFGIL6&issuer=Flexxer%20Backend&algorithm=SHA1&digits=6&period=30"
    },
    "errors": null,
    "meta": {
        "request_id": "e9e9b449-d901-409b-a9b4-fd071002aa0e",
        "timestamp": "2026-08-05T16:19:07.791377Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "method",
            "message": "The selected method is invalid."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/two-factor/enable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

method   string     

The 2FA method to enable. Example: totp

Must be one of:
  • email
  • totp. Allowed: email
  • totp

Confirm Two Factor

requires authentication

Confirms and activates 2FA after the user has verified their code.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/confirm" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/confirm');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication has been enabled. Save your recovery codes in a safe place.",
    "data": {
        "recovery_codes": [
            "DTPLO-U2SCF",
            "MRWS1-IKBUY",
            "MVM4F-Z42ES",
            "AUSXU-FTDPK",
            "U7WHX-TIU99",
            "12AWU-NZVH6",
            "ZY5XK-4WOKJ",
            "47MAL-ZSDLU"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "315aafcb-764e-4f38-9faa-b9c4f45e3461",
        "timestamp": "2026-08-05T16:19:07.807225Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, No Pending Setup):


{
    "status": "TWO_FACTOR_NO_PENDING_SETUP",
    "message": "No pending two-factor setup found. Please restart the setup process.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "No pending two-factor setup found. Please restart the setup process."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The code field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/two-factor/confirm

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string     

The OTP or TOTP code to verify. Example: 123456

Request Disable Two Factor Code

requires authentication

Dispatches a 6-digit OTP to the user's account email so they can confirm a 2FA-disable request. No-op for TOTP accounts (the code comes from the authenticator app directly) and for accounts without an enabled 2FA setting — both return 200 so the client's happy path is uniform.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable-code" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable-code"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable-code';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable-code');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, TOTP — use authenticator):


{
    "status": "SUCCESS",
    "message": "Enter the current code from your authenticator app.",
    "data": {
        "method": "totp"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Enter the current code from your authenticator app.",
    "data": {
        "method": "totp"
    },
    "errors": null,
    "meta": {
        "request_id": "0542b19f-0c0a-4bf3-93a4-d90bbac46f63",
        "timestamp": "2026-08-05T16:18:37.138042Z"
    }
}
 

Example response (409, Not Enabled):


{
    "status": "TWO_FACTOR_NOT_ENABLED",
    "message": "Two-factor authentication is not currently enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not currently enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/org/auth/two-factor/disable-code

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Disable Two Factor

requires authentication

Disables 2FA for the authenticated user after verifying a current challenge code — the same proof the user provides at login time.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/two-factor/disable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.delete(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication has been disabled.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "255f0d6e-8c58-4e47-84a4-c47dd821febf",
        "timestamp": "2026-08-05T16:19:07.817111Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (409, Not Enabled):


{
    "status": "TWO_FACTOR_NOT_ENABLED",
    "message": "Two-factor authentication is not currently enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not currently enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/org/auth/two-factor/disable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

6-digit TOTP or email OTP. Required unless recovery_code is provided. Example: 123456

recovery_code   string  optional    

Recovery code in place of code. Example: ABCD-1234-EFGH

Forgot Password

Sends a 4-digit password reset code to the given email address.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If that email address is registered, a password reset code has been sent.",
    "data": {
        "email": "opal56@example.net",
        "code": "448891"
    },
    "errors": null,
    "meta": {
        "request_id": "04e13c3e-6945-4f40-bb63-a330434430e3",
        "timestamp": "2026-08-05T16:18:54.164086Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "guard",
            "message": "The guard field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/forgot-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to send reset code to. Example: boris@example.com

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Verify Reset Code

Validates the 4-digit password reset code for the given email and guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/verify" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"code\": \"123456\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/verify"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "code": "123456",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'code' => '123456',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/verify');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "code": "123456",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Reset code verified successfully.",
    "data": {
        "reset_token": "c97701d0-c4de-4755-b3c4-f7d1a803efc5"
    },
    "errors": null,
    "meta": {
        "request_id": "67df2724-9771-46b5-8d4d-71f98573d3e5",
        "timestamp": "2026-08-05T16:18:54.166696Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "RESET_CODE_INVALID",
    "message": "Invalid reset code.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Invalid reset code."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (410, Code Expired):


{
    "status": "RESET_CODE_EXPIRED",
    "message": "Reset code has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Reset code has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The code field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Too Many Attempts):


{
    "status": "RESET_ATTEMPTS_EXHAUSTED",
    "message": "Too many failed attempts. Please request a new code.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Too many failed attempts. Please request a new code."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/forgot-password/verify

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address for password reset. Example: boris@example.com

code   string     

6-digit reset code. Example: 123456

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Resend Reset Code

Re-sends a 4-digit password reset code for the given email and guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/resend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/forgot-password/resend');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If that email address is registered, a new password reset code has been sent.",
    "data": {
        "code": "443677"
    },
    "errors": null,
    "meta": {
        "request_id": "a265e26e-388c-4782-b9ce-bc4722cc5958",
        "timestamp": "2026-08-05T16:18:58.095803Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "guard",
            "message": "The guard field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/forgot-password/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address to resend reset code to. Example: boris@example.com

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Reset Password

Resets the password for any actor type and revokes all existing tokens.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/reset-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"boris@example.com\",
    \"reset_token\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",
    \"password\": \"NewPassword123\",
    \"password_confirmation\": \"NewPassword123\",
    \"guard\": \"user\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/reset-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "boris@example.com",
    "reset_token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "password": "NewPassword123",
    "password_confirmation": "NewPassword123",
    "guard": "user"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/reset-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'boris@example.com',
            'reset_token' => 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
            'password' => 'NewPassword123',
            'password_confirmation' => 'NewPassword123',
            'guard' => 'user',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/reset-password');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "boris@example.com",
    "reset_token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "password": "NewPassword123",
    "password_confirmation": "NewPassword123",
    "guard": "user"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Password has been reset successfully.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "85c34424-2c5f-4d2e-aa97-5edf63d5d429",
        "timestamp": "2026-08-05T16:18:54.626775Z"
    }
}
 

Example response (403, Email Not Verified):


{
    "status": "RESET_NOT_VERIFIED",
    "message": "Email not verified. Complete the verification step first.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Email not verified. Complete the verification step first."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (404, Account Not Found):


{
    "status": "ACCOUNT_NOT_FOUND",
    "message": "Account not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Account not found."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "The password must be at least 8 characters."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Password Reuse):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "password",
            "message": "You cannot reuse your last 3 passwords."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/reset-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address for account. Example: boris@example.com

reset_token   string     

UUID from verify-reset-code step. Example: a1b2c3d4-e5f6-7890-abcd-ef1234567890

password   string     

New password (min 8, must be confirmed). Example: NewPassword123

password_confirmation   string     

Password confirmation. Example: NewPassword123

guard   string     

Actor type ("user", "organization", or "admin"). Example: user

Two Factor Challenge

requires authentication

Completes the two-factor authentication challenge for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/challenge" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/challenge"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/challenge';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/challenge');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication verified successfully.",
    "data": {
        "token": "42|wMrUbv7gI4CRWxcRbQIldttPzbyXBl2z181bI8Yt86612731",
        "refresh_token": "43|VPtqTDVPb3gY2MF4mIkms7vJcLWgUA47iwIxRI8e34e4f629",
        "expires_in": 900,
        "user": {
            "id": "019fd2b8-b86e-738d-8bfa-a29262a83558",
            "email": "hansen.mohammad@example.org"
        }
    },
    "errors": null,
    "meta": {
        "request_id": "0ff1a162-0832-4ffa-9700-d0f30d00a580",
        "timestamp": "2026-08-05T16:19:09.319799Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, 2FA Not Configured):


{
    "status": "TWO_FACTOR_NOT_CONFIGURED",
    "message": "Two-factor authentication is not configured for this account.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not configured for this account."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/two-factor/challenge

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

The OTP or TOTP code. Nullable if recovery_code is provided. Example: 123456

recovery_code   string  optional    

The recovery code. Nullable if code is provided. Example: ABCD-1234-EFGH

Logout

requires authentication

Revokes the currently active Sanctum token for any guard.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/logout" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/logout"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/logout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/logout');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (204, Success):

Empty response
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/logout

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Enable Two Factor

requires authentication

Initiates the 2FA setup flow for the authenticated user.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/enable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"method\": \"totp\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/enable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "method": "totp"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/enable';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'method' => 'totp',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/enable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "method": "totp"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Email):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication initiated.",
    "data": {
        "method": "email"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Scan the QR code with your authenticator app, then confirm with the generated code.",
    "data": {
        "method": "totp",
        "secret": "6RL4HDCP2F4CDU27SRQOWOS454GFGIL6",
        "qr_uri": "otpauth://totp/Flexxer%20Backend:019fd2b8-b36f-7307-92fb-5d28e58ef9f2?secret=6RL4HDCP2F4CDU27SRQOWOS454GFGIL6&issuer=Flexxer%20Backend&algorithm=SHA1&digits=6&period=30"
    },
    "errors": null,
    "meta": {
        "request_id": "e9e9b449-d901-409b-a9b4-fd071002aa0e",
        "timestamp": "2026-08-05T16:19:07.791377Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "method",
            "message": "The selected method is invalid."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/two-factor/enable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

method   string     

The 2FA method to enable. Example: totp

Must be one of:
  • email
  • totp. Allowed: email
  • totp

Confirm Two Factor

requires authentication

Confirms and activates 2FA after the user has verified their code.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/confirm" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/confirm');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication has been enabled. Save your recovery codes in a safe place.",
    "data": {
        "recovery_codes": [
            "DTPLO-U2SCF",
            "MRWS1-IKBUY",
            "MVM4F-Z42ES",
            "AUSXU-FTDPK",
            "U7WHX-TIU99",
            "12AWU-NZVH6",
            "ZY5XK-4WOKJ",
            "47MAL-ZSDLU"
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "315aafcb-764e-4f38-9faa-b9c4f45e3461",
        "timestamp": "2026-08-05T16:19:07.807225Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHORIZED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (409, No Pending Setup):


{
    "status": "TWO_FACTOR_NO_PENDING_SETUP",
    "message": "No pending two-factor setup found. Please restart the setup process.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "No pending two-factor setup found. Please restart the setup process."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The code field is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/two-factor/confirm

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string     

The OTP or TOTP code to verify. Example: 123456

Disable Two Factor

requires authentication

Disables 2FA for the authenticated user after verifying a current challenge code — the same proof the user provides at login time.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\",
    \"recovery_code\": \"ABCD-1234-EFGH\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
            'recovery_code' => 'ABCD-1234-EFGH',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "code": "123456",
    "recovery_code": "ABCD-1234-EFGH"
};

  final response = await http.delete(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Two-factor authentication has been disabled.",
    "data": null,
    "errors": null,
    "meta": {
        "request_id": "255f0d6e-8c58-4e47-84a4-c47dd821febf",
        "timestamp": "2026-08-05T16:19:07.817111Z"
    }
}
 

Example response (401, Invalid Code):


{
    "status": "TWO_FACTOR_CODE_INVALID",
    "message": "The provided code is invalid or has expired.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "The provided code is invalid or has expired."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (409, Not Enabled):


{
    "status": "TWO_FACTOR_NOT_ENABLED",
    "message": "Two-factor authentication is not currently enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not currently enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "code",
            "message": "Either code or recovery_code is required."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/admin/auth/two-factor/disable

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

code   string  optional    

6-digit TOTP or email OTP. Required unless recovery_code is provided. Example: 123456

recovery_code   string  optional    

Recovery code in place of code. Example: ABCD-1234-EFGH

Request Disable Two Factor Code

requires authentication

Dispatches a 6-digit OTP to the user's account email so they can confirm a 2FA-disable request. No-op for TOTP accounts (the code comes from the authenticator app directly) and for accounts without an enabled 2FA setting — both return 200 so the client's happy path is uniform.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable/request-code" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable/request-code"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable/request-code';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/auth/two-factor/disable/request-code');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, TOTP — use authenticator):


{
    "status": "SUCCESS",
    "message": "Enter the current code from your authenticator app.",
    "data": {
        "method": "totp"
    },
    "errors": null,
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Enter the current code from your authenticator app.",
    "data": {
        "method": "totp"
    },
    "errors": null,
    "meta": {
        "request_id": "0542b19f-0c0a-4bf3-93a4-d90bbac46f63",
        "timestamp": "2026-08-05T16:18:37.138042Z"
    }
}
 

Example response (409, Not Enabled):


{
    "status": "TWO_FACTOR_NOT_ENABLED",
    "message": "Two-factor authentication is not currently enabled.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Two-factor authentication is not currently enabled."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-22T12:00:00.000000Z"
    }
}
 

Request      

POST api/v1/admin/auth/two-factor/disable/request-code

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Sessions

List Sessions

requires authentication

Lists the authenticated user's (or org's, or admin's) active Sanctum sessions with their device metadata, sorted by last_used_at descending so the most recently active device is first.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/sessions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/sessions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/sessions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "id": 4,
            "name": "docs",
            "device": null,
            "ip_address": null,
            "last_used_at": "2026-08-05T16:18:37+00:00",
            "expires_at": null,
            "created_at": "2026-08-05T16:18:33+00:00",
            "is_current": true
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "b9c7f080-de76-4ae2-92d4-98f5f9c63e35",
        "timestamp": "2026-08-05T16:18:37.159008Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHENTICATED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

GET api/v1/mobile/sessions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Revoke Other Sessions

requires authentication

Logs out every OTHER active session of the authenticated caller, keeping the current one alive ("Alle anderen Geräte abmelden").

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/sessions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/sessions';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/sessions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "revoked": 0
    },
    "errors": null,
    "meta": {
        "request_id": "99342b48-bea9-49b3-a788-41e38c4c0892",
        "timestamp": "2026-08-05T16:18:37.170260Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHENTICATED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-18T12:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/mobile/sessions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Revoke Session

requires authentication

Revokes a single active session by id.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (204, Revoked):

Empty response
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHENTICATED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Session not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Session not found."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/mobile/sessions/{tokenId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tokenId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

sessionId   integer     

The ID of the session to revoke. Example: 16

List Sessions

requires authentication

Lists the authenticated user's (or org's, or admin's) active Sanctum sessions with their device metadata, sorted by last_used_at descending so the most recently active device is first.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/sessions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/sessions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/sessions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [
        {
            "id": 4,
            "name": "docs",
            "device": null,
            "ip_address": null,
            "last_used_at": "2026-08-05T16:18:37+00:00",
            "expires_at": null,
            "created_at": "2026-08-05T16:18:33+00:00",
            "is_current": true
        }
    ],
    "errors": null,
    "meta": {
        "request_id": "b9c7f080-de76-4ae2-92d4-98f5f9c63e35",
        "timestamp": "2026-08-05T16:18:37.159008Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHENTICATED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

GET api/v1/org/sessions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Revoke Other Sessions

requires authentication

Logs out every OTHER active session of the authenticated caller, keeping the current one alive ("Alle anderen Geräte abmelden").

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/org/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/sessions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/sessions';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/sessions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "revoked": 0
    },
    "errors": null,
    "meta": {
        "request_id": "99342b48-bea9-49b3-a788-41e38c4c0892",
        "timestamp": "2026-08-05T16:18:37.170260Z"
    }
}
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHENTICATED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-06-18T12:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/org/sessions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Revoke Session

requires authentication

Revokes a single active session by id.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/org/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/sessions/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (204, Revoked):

Empty response
 

Example response (401, Unauthenticated):


{
    "status": "UNAUTHENTICATED",
    "message": "Unauthenticated.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Unauthenticated."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Session not found.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Session not found."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-02T12:00:00.000000Z"
    }
}
 

Example response (429, Rate Limited):


{
    "status": "RATE_LIMITED",
    "message": "Too many requests.",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Too many requests."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T19:00:00.000000Z"
    }
}
 

Request      

DELETE api/v1/org/sessions/{tokenId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tokenId   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

sessionId   integer     

The ID of the session to revoke. Example: 16

requires authentication

Stores a new logo file for the authenticated organisation, replacing any existing one and returning the updated profile.

requires authentication

Removes the authenticated organisation's logo from storage and clears the logo_url field.

Privacy & GDPR

Lists GDPR Art. 12(3) requests submitted by the authenticated organisation.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "c161e586-954d-48ca-928d-41bad21a8adf",
        "timestamp": "2026-08-05T16:18:38.479808Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 0
    }
}
 

Request      

GET api/v1/org/me/gdpr-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Records a new GDPR Art. 12(3) request forwarded by an organisation on behalf of a data subject. Persists the request, emits an audit event, and notifies all super-admins via in-app and email.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"subject_email\": \"name@example.at\",
    \"request_type\": \"access\",
    \"message\": \"Bitte um Rueckmeldung zur naechsten Schicht.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subject_email": "name@example.at",
    "request_type": "access",
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'subject_email' => 'name@example.at',
            'request_type' => 'access',
            'message' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me/gdpr-requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "subject_email": "name@example.at",
    "request_type": "access",
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "CREATED",
    "message": "GDPR request submitted successfully.",
    "data": {
        "id": "019fd2b8-4116-7347-914a-230153f403ce",
        "request_type": "access",
        "request_type_label": "Access (Art. 15)",
        "subject_email": "name@example.at",
        "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
        "status": "pending",
        "status_label": "Pending",
        "submitted_at": "2026-08-05T16:18:38.000000Z",
        "resolved_at": null,
        "platform_note": null,
        "created_at": "2026-08-05T16:18:38.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "8f34c05d-27c1-4a19-b728-9442355514a1",
        "timestamp": "2026-08-05T16:18:38.490505Z"
    }
}
 

Request      

POST api/v1/org/me/gdpr-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

subject_email   string     

Example: name@example.at

request_type   string     

Allowed: access, erasure, restriction, rectification. Example: access

message   string  optional    

Example: Bitte um Rueckmeldung zur naechsten Schicht.

GDPR Art. 15 (Right of Access) / Art. 20 (Right to Data Portability).

requires authentication

Returns a structured JSON export of the organisation's platform data: campaigns (with images), donations received, recurring donations, and team members. Donor PII is masked — only display names and masked email addresses are included.

Austrian DSG §1(1) + GDPR Art. 12(3): response within one month, delivered immediately here as structured JSON.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/me/data-export" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me/data-export"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me/data-export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me/data-export');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Data export generated.",
    "data": {
        "organisation": {
            "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "name": "Caritas",
            "legal_name": null,
            "legal_form": null,
            "tax_id": null,
            "email": "caritas-portal@flexxr.eu.cc",
            "phone": "+436641234567",
            "website": "https://www.caritas.at",
            "country": "Austria",
            "city": "Vienna",
            "address": "Albrechtskreithgasse 19-21",
            "postal_code": null,
            "created_at": "2026-08-05T16:18:17+00:00"
        },
        "team_members": [
            {
                "id": "019fd2b8-285e-707c-865a-ffbe4b08f730",
                "role": "admin",
                "is_active": true,
                "joined_at": "2026-08-05T16:18:32+00:00"
            }
        ],
        "exported_at": "2026-08-05T16:18:38+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "c29cdf34-d349-42b5-9f9b-af7de00d12c4",
        "timestamp": "2026-08-05T16:18:38.498181Z"
    }
}
 

Request      

GET api/v1/org/me/data-export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Lists GDPR Art. 12(3) requests submitted by the authenticated organisation.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/gdpr/requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/gdpr/requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/gdpr/requests';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/gdpr/requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "c161e586-954d-48ca-928d-41bad21a8adf",
        "timestamp": "2026-08-05T16:18:38.479808Z",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 0
    }
}
 

Request      

GET api/v1/org/gdpr/requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Records a new GDPR Art. 12(3) request forwarded by an organisation on behalf of a data subject. Persists the request, emits an audit event, and notifies all super-admins via in-app and email.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/gdpr/requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"subject_email\": \"name@example.at\",
    \"request_type\": \"access\",
    \"message\": \"Bitte um Rueckmeldung zur naechsten Schicht.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/gdpr/requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subject_email": "name@example.at",
    "request_type": "access",
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/gdpr/requests';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'subject_email' => 'name@example.at',
            'request_type' => 'access',
            'message' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/gdpr/requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "subject_email": "name@example.at",
    "request_type": "access",
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "CREATED",
    "message": "GDPR request submitted successfully.",
    "data": {
        "id": "019fd2b8-4116-7347-914a-230153f403ce",
        "request_type": "access",
        "request_type_label": "Access (Art. 15)",
        "subject_email": "name@example.at",
        "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
        "status": "pending",
        "status_label": "Pending",
        "submitted_at": "2026-08-05T16:18:38.000000Z",
        "resolved_at": null,
        "platform_note": null,
        "created_at": "2026-08-05T16:18:38.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "8f34c05d-27c1-4a19-b728-9442355514a1",
        "timestamp": "2026-08-05T16:18:38.490505Z"
    }
}
 

Request      

POST api/v1/org/gdpr/requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

subject_email   string     

Example: name@example.at

request_type   string     

Allowed: access, erasure, restriction, rectification. Example: access

message   string  optional    

Example: Bitte um Rueckmeldung zur naechsten Schicht.

Privacy

requires authentication

Returns a paginated list of cookie-consent decisions recorded for the authenticated organisation. Backed by the immutable audit log (GDPR Art. 15 right of access).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/me/consent-history?per_page=25" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me/consent-history"
);

const params = {
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me/consent-history';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '25',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me/consent-history')
      .replace(queryParameters: {
        'per_page': '25',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "a726ed83-b8fd-4332-9cfe-5ef5302a22f0",
        "timestamp": "2026-08-05T16:18:38.519100Z",
        "total": 0,
        "current_page": 1,
        "last_page": 1,
        "per_page": 25
    }
}
 

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/me/consent" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"version\": \"2026-04-01\",
    \"choices\": {
        \"necessary\": true,
        \"analytics\": false,
        \"marketing\": false
    },
    \"timestamp\": \"2026-04-21T10:00:00.000Z\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/me/consent"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "version": "2026-04-01",
    "choices": {
        "necessary": true,
        "analytics": false,
        "marketing": false
    },
    "timestamp": "2026-04-21T10:00:00.000Z"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/me/consent';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'version' => '2026-04-01',
            'choices' => ['necessary' => true, 'analytics' => false, 'marketing' => false],
            'timestamp' => '2026-04-21T10:00:00.000Z',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/me/consent');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "version": "2026-04-01",
    "choices": {
        "necessary": true,
        "analytics": false,
        "marketing": false
    },
    "timestamp": "2026-04-21T10:00:00.000Z"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Consent recorded.",
    "data": {
        "recorded_at": "2026-08-05T16:18:38+00:00"
    },
    "errors": null,
    "meta": {
        "request_id": "5a2c4cdb-a5b7-4ed0-adc0-6d872d01255a",
        "timestamp": "2026-08-05T16:18:38.527726Z"
    }
}
 

Example response (422, Validation Error):


{
    "status": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "data": null,
    "errors": [
        {
            "field": "choices.necessary",
            "message": "The choices.necessary field must be accepted."
        }
    ],
    "meta": {
        "request_id": "550e8400-e29b-41d4-a716-446655440000",
        "timestamp": "2026-04-21T10:00:00.000000Z"
    }
}
 

Audit

List Org Audit Log

requires authentication

A paginated view of everything the authenticated organisation did, had done to it, or had done on its behalf. A row is in scope when any of the following holds:

a) the organisation is the actor; b) the organisation is the subject; c) the subject is one of its members; or d) one of its members is the actor — most of a company's work is done by the people it invited, so without this the log showed almost none of the company's real activity.

The event filter accepts a value from the allowed list, which excludes admin-internal and user-PII events so they are never exposed here.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/audit-log?event=model.updated&from=2026-01-01&to=2026-12-31&page=1&per_page=25" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"event\": \"architecto\",
    \"from\": \"2026-08-21T05:14:34\",
    \"to\": \"2052-09-13\",
    \"page\": 22,
    \"per_page\": 67
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/audit-log"
);

const params = {
    "event": "model.updated",
    "from": "2026-01-01",
    "to": "2026-12-31",
    "page": "1",
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "event": "architecto",
    "from": "2026-08-21T05:14:34",
    "to": "2052-09-13",
    "page": 22,
    "per_page": 67
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/audit-log';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'event' => 'model.updated',
            'from' => '2026-01-01',
            'to' => '2026-12-31',
            'page' => '1',
            'per_page' => '25',
        ],
        'json' => [
            'event' => 'architecto',
            'from' => '2026-08-21T05:14:34',
            'to' => '2052-09-13',
            'page' => 22,
            'per_page' => 67,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/audit-log')
      .replace(queryParameters: {
        'event': 'model.updated',
        'from': '2026-01-01',
        'to': '2026-12-31',
        'page': '1',
        'per_page': '25',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "event": "architecto",
    "from": "2026-08-21T05:14:34",
    "to": "2052-09-13",
    "page": 22,
    "per_page": 67
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Audit log retrieved.",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "24a91099-8b31-42b2-ae2c-6bcd6450f1fd",
        "timestamp": "2026-08-05T16:18:38.535487Z",
        "current_page": 22,
        "last_page": 1,
        "per_page": 67,
        "total": 0
    }
}
 

Request      

GET api/v1/org/audit-log

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

event   string  optional    

nullable Filter by audit event value (must be in the allowed list). Example: model.updated

from   string  optional    

nullable Inclusive ISO date. Example: 2026-01-01

to   string  optional    

nullable Inclusive ISO date. Example: 2026-12-31

page   integer  optional    

Page number. Example: 1

per_page   integer  optional    

Rows per page (1–100, default 25). Example: 25

Body Parameters

event   string  optional    

Example: architecto

from   string  optional    

Must be a valid date. Example: 2026-08-21T05:14:34

to   string  optional    

Must be a valid date. Must be a date after or equal to from. Example: 2052-09-13

page   integer  optional    

Must be at least 1. Example: 22

per_page   integer  optional    

Must be at least 1. Example: 67

Security

Get Two-Factor State

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/security/two-factor" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/security/two-factor"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/security/two-factor';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/security/two-factor');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": {
        "enabled": true,
        "method": "totp",
        "confirmed_at": "2026-08-05T16:18:32+00:00",
        "recovery_codes_remaining": 2
    },
    "errors": null,
    "meta": {
        "request_id": "3f0ace2f-07fd-4e6e-9bd0-24b9619f2e8e",
        "timestamp": "2026-08-05T16:18:38.604346Z"
    }
}
 

Request      

GET api/v1/org/security/two-factor

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

List Login History

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/security/login-history?limit=50" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/security/login-history"
);

const params = {
    "limit": "50",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/security/login-history';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'limit' => '50',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/security/login-history')
      .replace(queryParameters: {
        'limit': '50',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Success",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "a1ef4b94-6775-44ca-996f-3a02b14c2a73",
        "timestamp": "2026-08-05T16:18:38.610416Z"
    }
}
 

Request      

GET api/v1/org/security/login-history

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

limit   integer  optional    

nullable Maximum entries to return (min 1, max 200, default 50). Example: 50

Kyc Status

Get Organisation KYC Status

requires authentication

Returns the KYC checklist status for the authenticated organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/kyc-status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/kyc-status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/kyc-status';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/kyc-status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "KYC-Status erfolgreich abgerufen.",
    "data": {
        "organization": {
            "id": "019fd2b7-f072-7154-8961-5b324cc6c46c",
            "status": "active",
            "statusLabel": "Active",
            "rejectionReason": null,
            "verifiedAt": "2026-08-05T16:18:17+00:00"
        },
        "summary": {
            "requiredCount": 5,
            "uploadedCount": 0,
            "approvedCount": 0,
            "rejectedCount": 0,
            "missingCount": 5,
            "completionPercent": 0,
            "isReadyForReview": false,
            "nextAction": "upload_missing"
        },
        "checklist": [
            {
                "type": "gewerbeschein",
                "label": "Gewerbeschein",
                "description": null,
                "required": true,
                "source": "baseline",
                "status": "missing",
                "latestDocument": null,
                "historyCount": 0,
                "openRequestId": null
            },
            {
                "type": "firmenbuch_auszug",
                "label": "Firmenbuchauszug",
                "description": null,
                "required": true,
                "source": "baseline",
                "status": "missing",
                "latestDocument": null,
                "historyCount": 0,
                "openRequestId": null
            },
            {
                "type": "aueg_bewilligung",
                "label": "AÜG-Bewilligung",
                "description": null,
                "required": true,
                "source": "baseline",
                "status": "missing",
                "latestDocument": null,
                "historyCount": 0,
                "openRequestId": null
            },
            {
                "type": "ausweis_gf",
                "label": "Ausweis Geschäftsführer",
                "description": null,
                "required": true,
                "source": "baseline",
                "status": "missing",
                "latestDocument": null,
                "historyCount": 0,
                "openRequestId": null
            },
            {
                "type": "unterschriftenprobe",
                "label": "Unterschriftenprobe",
                "description": null,
                "required": true,
                "source": "baseline",
                "status": "missing",
                "latestDocument": null,
                "historyCount": 0,
                "openRequestId": null
            },
            {
                "type": "uid_bestaetigung",
                "label": "UID-Bestätigung",
                "description": null,
                "required": false,
                "source": "baseline",
                "status": "missing",
                "latestDocument": null,
                "historyCount": 0,
                "openRequestId": null
            }
        ],
        "openRequests": []
    },
    "errors": null,
    "meta": {
        "request_id": "673ba49d-0208-4a7a-8bd6-ccba99d10ede",
        "timestamp": "2026-08-05T16:18:38.651604Z"
    }
}
 

Request      

GET api/v1/org/kyc-status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Documents

List Organization Documents

requires authentication

Lists all non-deleted KYC documents belonging to the authenticated organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Dokumente erfolgreich abgerufen.",
    "data": [],
    "errors": null,
    "meta": {
        "request_id": "f421c038-b8cf-43c0-ab3b-142d37da1232",
        "timestamp": "2026-08-05T16:18:38.658086Z"
    }
}
 

Request      

GET api/v1/org/documents

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Upload Organization Document

requires authentication

Accepts a KYC document upload from the authenticated organisation.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "type=Beispieltext"\
    --form "document_request_id=019f9939-c4c3-70fb-a54e-5ebf63db36d2"\
    --form "file=@/tmp/php9itgkdkhc241c2Z59vG" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('type', 'Beispieltext');
body.append('document_request_id', '019f9939-c4c3-70fb-a54e-5ebf63db36d2');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/documents';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'type',
                'contents' => 'Beispieltext'
            ],
            [
                'name' => 'document_request_id',
                'contents' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/php9itgkdkhc241c2Z59vG', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.fields['type'] = 'Beispieltext';
  request.fields['document_request_id'] = '019f9939-c4c3-70fb-a54e-5ebf63db36d2';
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/php9itgkdkhc241c2Z59vG'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/documents

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

type   string     

Example: Beispieltext

file   file     

Example: /tmp/php9itgkdkhc241c2Z59vG

document_request_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Download Organization Document

requires authentication

Streams a KYC document back to the owning organisation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5/download" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5/download"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5/download';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5/download');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "b21dce97-33bd-4cbb-a28c-67af75128d72",
        "timestamp": "2026-08-21T05:14:34.591593Z"
    }
}
 

Request      

GET api/v1/org/documents/{document_id}/download

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

document_id   string     

The ID of the document. Example: 019f34d3-5029-7158-a549-910ae39abad5

document   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Delete Organization Document

requires authentication

Soft-deletes a Pending KYC document belonging to the authenticated organisation.

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/documents/019f34d3-5029-7158-a549-910ae39abad5');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/org/documents/{document_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

document_id   string     

The ID of the document. Example: 019f34d3-5029-7158-a549-910ae39abad5

document   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

requires authentication

Lists the legal documents the organisation must accept, the current acceptance state per type, and the full acceptance history (for the "Legal documents" page).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/legal-documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/legal-documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/legal-documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/legal-documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Rechtsdokumente erfolgreich abgerufen.",
    "data": {
        "documents": [
            {
                "type": "privacy_policy",
                "label": "Datenschutzerklärung",
                "locale": "de",
                "currentVersion": 1,
                "currentVersionId": "019fd2b7-ef68-7312-a1a3-6b14820cf9e2",
                "acceptedVersion": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "status": "accepted",
                "viewUrl": "http://localhost:8001/legal/privacy_policy/de/v/1"
            },
            {
                "type": "terms_of_service",
                "label": "Allgemeine Geschäftsbedingungen",
                "locale": "de",
                "currentVersion": 1,
                "currentVersionId": "019fd2b7-ef6f-72ae-8c86-d9977da26ec0",
                "acceptedVersion": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "status": "accepted",
                "viewUrl": "http://localhost:8001/legal/terms_of_service/de/v/1"
            },
            {
                "type": "data_processing",
                "label": "Auftragsverarbeitung",
                "locale": "de",
                "currentVersion": 1,
                "currentVersionId": "019fd2b7-ef76-73b5-a9c5-d7cca778324b",
                "acceptedVersion": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "status": "accepted",
                "viewUrl": "http://localhost:8001/legal/data_processing/de/v/1"
            },
            {
                "type": "svnr_consent",
                "label": "Einwilligung zur SV-Nummer",
                "locale": "de",
                "currentVersion": 1,
                "currentVersionId": "019fd2b7-ef83-7298-a78c-af4b1e36b67b",
                "acceptedVersion": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "status": "accepted",
                "viewUrl": "http://localhost:8001/legal/svnr_consent/de/v/1"
            }
        ],
        "history": [
            {
                "type": "svnr_consent",
                "label": "Einwilligung zur SV-Nummer",
                "version": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "withdrawnAt": null
            },
            {
                "type": "data_processing",
                "label": "Auftragsverarbeitung",
                "version": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "withdrawnAt": null
            },
            {
                "type": "terms_of_service",
                "label": "Allgemeine Geschäftsbedingungen",
                "version": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "withdrawnAt": null
            },
            {
                "type": "privacy_policy",
                "label": "Datenschutzerklärung",
                "version": 1,
                "acceptedAt": "2026-08-05T16:18:32+00:00",
                "withdrawnAt": null
            }
        ]
    },
    "errors": null,
    "meta": {
        "request_id": "5088afd2-6632-4150-9a93-7a8d53e7d090",
        "timestamp": "2026-08-05T16:18:38.691599Z"
    }
}
 

requires authentication

Returns the legal document versions the organisation must still accept.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/legal-documents/pending" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/legal-documents/pending"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/legal-documents/pending';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/legal-documents/pending');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Ausstehende Rechtsdokumente erfolgreich abgerufen.",
    "data": {
        "pending": []
    },
    "errors": null,
    "meta": {
        "request_id": "8b96ae8c-9c5d-435e-a7ab-974f30899fbb",
        "timestamp": "2026-08-05T16:18:38.707936Z"
    }
}
 

requires authentication

Records the organisation's acceptance of the current version of a legal document type, pinning the exact version for audit.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/legal-documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/accept" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/legal-documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/accept"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/legal-documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/accept';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/legal-documents/019f9939-c4c3-70fb-a54e-5ebf63db36d2/accept');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Contract Template

Get Contract Template

requires authentication

The organisation's default contract template for the settings editor: the saved organisation-level row when one exists, otherwise the built-in default body — plus the merge fields the editor can insert. Per-job and per-shift overrides have their own endpoints ({@see GetJobContractTemplateAction}, {@see GetShiftContractTemplateAction}); this is always the organisation default level (job_id and shift_id both null).

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/contract-template" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/contract-template"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/contract-template';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/contract-template');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "template": {
            "name": "Überlassungsvertrag",
            "body_html": "<h2>Arbeitskraft</h2>\n<p>{worker_name}, geboren am {worker_birth_date}, {worker_address}</p>\n<h2>Beschäftiger</h2>\n<p>{organization_name}, {organization_address}</p>\n<h2>Überlassung</h2>\n<p>Tätigkeit: {job_title}<br>Einsatzort: {location}<br>Zeitraum: {start_date} – {end_date}<br>Entgelt (brutto/Std.): {hourly_rate}<br>Kollektivvertrag: {collective_agreement}</p>\n<p>Für die Dauer der Überlassung gelten gemäß § 10 AÜG die im Beschäftigerbetrieb für vergleichbare Arbeitnehmer geltenden wesentlichen Arbeits- und Beschäftigungsbedingungen. Rechtsgrundlage: {legal_basis}.</p>",
            "version": 0,
            "is_active": false,
            "is_customized": false
        },
        "merge_fields": [
            "contract_number",
            "worker_name",
            "worker_birth_date",
            "worker_nationality",
            "worker_address",
            "organization_name",
            "organization_address",
            "job_title",
            "job_description",
            "location",
            "start_date",
            "end_date",
            "hourly_rate",
            "collective_agreement",
            "legal_basis"
        ]
    }
}
 

Request      

GET api/v1/org/contract-template

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Contract Template

requires authentication

Save the organisation's default contract template (job_id and shift_id both null). Upserts the single per-org row, bumps its version, and strips active content (script/style) from the HTML — the body is rendered into the worker's signed PDF unless a job or shift override applies.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/contract-template" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Beispieltext\",
    \"body_html\": \"Bitte um Rueckmeldung zur naechsten Schicht.\",
    \"is_active\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/contract-template"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Beispieltext",
    "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_active": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/contract-template';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Beispieltext',
            'body_html' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
            'is_active' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/contract-template');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "name": "Beispieltext",
    "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
    "is_active": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The contract template has been saved.",
    "data": {
        "template": {
            "name": "Beispieltext",
            "body_html": "Bitte um Rueckmeldung zur naechsten Schicht.",
            "version": 1,
            "is_active": true,
            "is_customized": true
        }
    }
}
 

Request      

PUT api/v1/org/contract-template

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string  optional    

Example: Beispieltext

body_html   string     

Example: Bitte um Rueckmeldung zur naechsten Schicht.

is_active   boolean  optional    

Example: true

Messages

List Conversations

requires authentication

List conversations for organization staff.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/messages/conversations" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/messages/conversations"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/messages/conversations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/messages/conversations');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "conversations": [
            {
                "id": "64c3f8d8-55d2-4457-b394-c3277a378bac",
                "participant": {
                    "id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "name": "Anna Neuling",
                    "avatar_url": null
                },
                "job": null,
                "last_message": null,
                "last_message_is_from_user": null,
                "last_message_at": "2026-08-05T16:18:32+00:00",
                "unread_count": 0
            }
        ]
    }
}
 

Request      

GET api/v1/org/messages/conversations

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Messages

requires authentication

Get messages for a conversation.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019fd51d-53c7-73d0-880d-8edee1800329');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "conversation": {
            "id": "64c3f8d8-55d2-4457-b394-c3277a378bac",
            "participant": {
                "id": "019fd2b7-f271-73fd-8268-848be381e136",
                "name": "Anna Neuling",
                "avatar_url": null
            },
            "job": null
        },
        "messages": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 50,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/org/messages/conversations/{conversation_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

conversation_id   string     

The ID of the conversation. Example: 019fd51d-53c7-73d0-880d-8edee1800329

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Mark Conversation Read

requires authentication

Mark a conversation as read by the organization.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/read" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/read"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/read';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/read');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Marked read):


{
    "status": "SUCCESS"
}
 

Request      

POST api/v1/org/messages/conversations/{conversation}/read

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Upload a Message Attachment

requires authentication

Upload a file attachment ahead of sending it with a message. The returned attachment id is bound to a message when that message is sent.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "file=@/tmp/phpqtm74uqeke1f9ag7iaC" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'file',
                'contents' => fopen('/tmp/phpqtm74uqeke1f9ag7iaC', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/messages/conversations/019f9939-c4c3-70fb-a54e-5ebf63db36d2/attachments');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.files.add(await http.MultipartFile.fromPath('file', '/tmp/phpqtm74uqeke1f9ag7iaC'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Example response (201, Uploaded):


{
    "status": "SUCCESS",
    "data": {
        "attachment": {
            "id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
            "name": "beleg.pdf",
            "mime_type": "application/pdf",
            "file_size": 48213
        }
    }
}
 

Request      

POST api/v1/org/messages/conversations/{conversation}/attachments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

conversation   string     

Identifier from the collection. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

file   file     

The photo or document. Example: /tmp/phpqtm74uqeke1f9ag7iaC

Send Message

requires authentication

Send a message to an employee.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/messages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"user_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"content\": \"Beispieltext\",
    \"job_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"shift_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"attachments\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "content": "Beispieltext",
    "job_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "shift_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "attachments": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'content' => 'Beispieltext',
            'job_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'shift_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'attachments' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/messages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "content": "Beispieltext",
    "job_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "shift_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "attachments": null
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "message": {
            "id": "019fd2b8-4623-72c9-81f4-41013427b344",
            "conversation_id": "019fd2b8-4620-7315-bdd5-20f236d38c04",
            "content": "Beispieltext",
            "created_at": "2026-08-05T16:18:39+00:00"
        }
    }
}
 

Request      

POST api/v1/org/messages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

user_id   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

content   string  optional    

Required unless attachments are present. Example: Beispieltext

job_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

shift_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

attachments   string[]  optional    

IDs from the attachment upload endpoint.

Audit Log

Record Organisation Pii Reveal

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/audit-log/pii-reveal" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"module\": \"Beispieltext\",
    \"context\": \"Beispieltext\",
    \"subject_type\": \"Frage zur Abrechnung\",
    \"subject_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\",
    \"fields\": null
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/audit-log/pii-reveal"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "module": "Beispieltext",
    "context": "Beispieltext",
    "subject_type": "Frage zur Abrechnung",
    "subject_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "fields": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/audit-log/pii-reveal';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'module' => 'Beispieltext',
            'context' => 'Beispieltext',
            'subject_type' => 'Frage zur Abrechnung',
            'subject_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
            'fields' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/audit-log/pii-reveal');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "module": "Beispieltext",
    "context": "Beispieltext",
    "subject_type": "Frage zur Abrechnung",
    "subject_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
    "fields": null
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "PII reveal has been audit logged."
}
 

Request      

POST api/v1/org/audit-log/pii-reveal

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

module   string     

Example: Beispieltext

context   string  optional    

Example: Beispieltext

subject_type   string  optional    

Example: Frage zur Abrechnung

subject_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

fields   string[]  optional    

Support

List Support Tickets

requires authentication

The tickets visible to the caller, newest first, optionally narrowed by status.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/support/tickets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/support/tickets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/support/tickets';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/support/tickets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "tickets": [
            {
                "id": "019fd2b8-28b2-7322-b70c-bc7d76debfc3",
                "ticket_number": "TKT-717858",
                "subject": "Nihil autem neque blanditiis accusantium eaque.",
                "category": "general",
                "category_label": "Allgemein",
                "status": "open",
                "status_label": "Offen",
                "priority": 3,
                "requester_type": "company",
                "requester_name": "Caritas",
                "assignee_name": null,
                "message_count": 0,
                "last_message_at": null,
                "created_at": "2026-08-05T16:18:32+00:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/org/support/tickets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Open a Support Ticket

requires authentication

Raises a new support ticket with a subject and an opening message.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/support/tickets" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"category\": \"general\",
    \"subject\": \"Frage zur Abrechnung\",
    \"description\": \"Beispieltext\",
    \"related_entity_type\": \"shift\",
    \"related_entity_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/support/tickets"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "category": "general",
    "subject": "Frage zur Abrechnung",
    "description": "Beispieltext",
    "related_entity_type": "shift",
    "related_entity_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/support/tickets';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'category' => 'general',
            'subject' => 'Frage zur Abrechnung',
            'description' => 'Beispieltext',
            'related_entity_type' => 'shift',
            'related_entity_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/support/tickets');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "category": "general",
    "subject": "Frage zur Abrechnung",
    "description": "Beispieltext",
    "related_entity_type": "shift",
    "related_entity_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/support/tickets

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

category   string     

Allowed: general, shift, payment, contract, account, technical, complaint, profile_change, document, other. Example: general

subject   string     

Example: Frage zur Abrechnung

description   string     

Example: Beispieltext

related_entity_type   string  optional    

Allowed: shift, contract, job. Example: shift

related_entity_id   string  optional    

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Show a Support Ticket

requires authentication

One ticket with its message thread. Internal notes are never included.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "id": "019fd2b8-28b2-7322-b70c-bc7d76debfc3",
        "ticket_number": "TKT-717858",
        "subject": "Nihil autem neque blanditiis accusantium eaque.",
        "category": "general",
        "category_label": "Allgemein",
        "status": "open",
        "status_label": "Offen",
        "priority": 3,
        "requester_type": "company",
        "requester_name": "Caritas",
        "assignee_name": null,
        "message_count": 0,
        "last_message_at": null,
        "created_at": "2026-08-05T16:18:32+00:00",
        "description": "Quo necessitatibus et tempora esse nemo. Omnis et molestias sed expedita cupiditate.\n\nQuidem mollitia et reprehenderit enim quidem est. Temporibus et blanditiis iusto officia neque molestiae quidem. Adipisci suscipit sit temporibus voluptatum porro molestiae omnis. Rem fuga ratione ratione odio esse odio modi.",
        "resolution_notes": null,
        "related_entity_type": null,
        "related_entity_id": null,
        "assigned_to": null,
        "first_response_at": null,
        "resolved_at": null,
        "messages": []
    }
}
 

Request      

GET api/v1/org/support/tickets/{ticketId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

The ticket. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Reply to a Support Ticket

requires authentication

Appends a message to an existing ticket.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"message\": \"Bitte um Rueckmeldung zur naechsten Schicht.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'message' => 'Bitte um Rueckmeldung zur naechsten Schicht.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/support/tickets/019f9939-c4c3-70fb-a54e-5ebf63db36d2/messages');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "message": "Bitte um Rueckmeldung zur naechsten Schicht."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Your message has been sent.",
    "data": {
        "message": {
            "id": "019fd2b8-46aa-7182-9c38-e64d58e94097",
            "message": "Bitte um Rueckmeldung zur naechsten Schicht.",
            "sender_type": "company",
            "sender_name": "Caritas",
            "is_internal": false,
            "attachments": null,
            "created_at": "2026-08-05T16:18:39+00:00"
        }
    }
}
 

Request      

POST api/v1/org/support/tickets/{ticketId}/messages

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

ticketId   string     

The ticket. Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

Body Parameters

message   string     

Example: Bitte um Rueckmeldung zur naechsten Schicht.

Admin - Employees

Admin dashboard endpoints for platform management. Requires admin-level Bearer token authentication.

Employees

Apply a sanction to a worker who was sent home or breached the rules. The admin chooses the consequence: suspension days, a payment treatment (full / partial / none / goodwill) and/or a warning (Ermahnung).

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/sanction" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Wiederholtes Zuspätkommen\",
    \"suspension_days\": 7,
    \"payment_treatment\": \"partial\",
    \"issue_warning\": true,
    \"shift_id\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/sanction"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Wiederholtes Zuspätkommen",
    "suspension_days": 7,
    "payment_treatment": "partial",
    "issue_warning": true,
    "shift_id": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/sanction';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Wiederholtes Zuspätkommen',
            'suspension_days' => 7,
            'payment_treatment' => 'partial',
            'issue_warning' => true,
            'shift_id' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/employees/architecto/sanction');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Wiederholtes Zuspätkommen",
    "suspension_days": 7,
    "payment_treatment": "partial",
    "issue_warning": true,
    "shift_id": "architecto"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/employees/{profileId}/sanction

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

profileId   string     

Example: architecto

Body Parameters

reason   string     

Shared reason for the sanction. Example: Wiederholtes Zuspätkommen

suspension_days   integer  optional    

Days to suspend the worker. Example: 7

payment_treatment   string  optional    

Allowed: full, partial, none, goodwill. Example: partial

issue_warning   boolean  optional    

Send a warning notification. Example: true

shift_id   string  optional    

Optional related shift UUID. Example: architecto

Admin - LHR Payroll

Admin dashboard endpoints for platform management. Requires admin-level Bearer token authentication.

Lhr

Request Payroll Approval

requires authentication

Opens a four-eyes approval request for a payroll period. A different admin with admin.payroll.trigger must then approve it before the payroll trigger endpoint will accept the approval_id.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"period\": \"2026-06\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "period": "2026-06"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'period' => '2026-06',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "period": "2026-06"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (201, Recorded from a live call):


{
    "status": "CREATED",
    "message": "Approval request opened.",
    "data": {
        "id": "019fd2b8-53e2-72c9-afe8-17239fd1a8e1",
        "period": "2026-06",
        "status": "pending",
        "requested_by_id": "019fd2b7-edab-7280-b09f-b18f58a272ee",
        "created_at": "2026-08-05T16:18:43.000000Z"
    },
    "errors": null,
    "meta": {
        "request_id": "b7e77921-3f4d-4d85-83af-84afa8e7b97e",
        "timestamp": "2026-08-05T16:18:43.305161Z"
    }
}
 

Example response (409):


{
    "status": "CONFLICT",
    "message": "An open approval request already exists for this period."
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "This period has already been executed."
}
 

Request      

POST api/v1/admin/lhr/payroll/approval-requests

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

period   string     

Payroll period in YYYY-MM format. Example: 2026-06

Approve Payroll Approval Request

requires authentication

Approves a pending four-eyes payroll approval request. The approver must be a different admin from the requester.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.put(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200):


{
    "status": "SUCCESS",
    "message": "Approval granted.",
    "data": {
        "id": "uuid",
        "period": "2026-06",
        "status": "approved",
        "approved_by_id": "uuid",
        "approved_at": "2026-06-14T10:05:00.000000Z"
    }
}
 

Example response (403):


{
    "status": "FORBIDDEN",
    "message": "The requester cannot approve their own request."
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "This approval request is not in a pending state."
}
 

Request      

PUT api/v1/admin/lhr/payroll/approval-requests/{approval_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

approval_id   string     

The ID of the approval. Example: architecto

approval   string     

UUID of the approval request. Example: 550e8400-e29b-41d4-a716-446655440000

Reject Payroll Approval Request

requires authentication

Declines a pending four-eyes payroll approval request, closing the period to payroll until a new request is raised. The reviewer must be a different admin from the requester, and must say why — the requester needs to know what to change before asking again.

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto/reject" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Overtime for the Graz site is still unconfirmed.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto/reject"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Overtime for the Graz site is still unconfirmed."
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto/reject';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Overtime for the Graz site is still unconfirmed.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/approval-requests/architecto/reject');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Overtime for the Graz site is still unconfirmed."
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200):


{
    "status": "SUCCESS",
    "message": "Approval rejected.",
    "data": {
        "id": "uuid",
        "period": "2026-06",
        "status": "rejected",
        "approved_by_id": "uuid",
        "approved_at": "2026-06-14T10:05:00.000000Z",
        "rejection_reason": "Overtime for the Graz site is still unconfirmed."
    }
}
 

Example response (403):


{
    "status": "FORBIDDEN",
    "message": "The requester cannot approve their own request."
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "This approval request is not in a pending state."
}
 

Request      

PUT api/v1/admin/lhr/payroll/approval-requests/{approval_id}/reject

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

approval_id   string     

The ID of the approval. Example: architecto

approval   string     

UUID of the approval request. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

reason   string     

Why the request was declined (max 1000). Example: Overtime for the Graz site is still unconfirmed.

Trigger LHR payroll.

requires authentication

Trigger monthly payroll calculation in LHR.

Requires a pre-approved four-eyes approval record. The approval_id must reference a PayrollApproval in approved state for the same period, approved by a different admin than the caller who requested it.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/trigger" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"period\": \"2026-01\",
    \"approval_id\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/trigger"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "period": "2026-01",
    "approval_id": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/trigger';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'period' => '2026-01',
            'approval_id' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/trigger');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "period": "2026-01",
    "approval_id": "architecto"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (202):


{
    "status": "success",
    "message": "Payroll calculation triggered",
    "data": {
        "abrechnung_id": "abc-123",
        "period": "2026-01",
        "status": "queued"
    }
}
 

Example response (422):


{
    "status": "VALIDATION_ERROR",
    "message": "Approval is not in an approved state."
}
 

Example response (503):


{
    "status": "error",
    "message": "LHR service unavailable"
}
 

Request      

POST api/v1/admin/lhr/payroll/trigger

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

period   string     

The payroll period in YYYY-MM format. Example: 2026-01

approval_id   string     

UUID of an approved PayrollApproval for this period. Example: architecto

Get payroll calculation status from LHR.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/status';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/status');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "83b111e4-96dc-4495-ba19-ba40d5076dc9",
        "timestamp": "2026-08-21T05:14:36.862690Z"
    }
}
 

Request      

GET api/v1/admin/lhr/payroll/{abrechnungId}/status

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

abrechnungId   string     

Example: architecto

Get payroll results from LHR.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/results" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/results"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/results';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/payroll/architecto/results');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "524f03f5-0c13-41af-82bd-26b21bef8af1",
        "timestamp": "2026-08-21T05:14:36.871506Z"
    }
}
 

Request      

GET api/v1/admin/lhr/payroll/{abrechnungId}/results

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

abrechnungId   string     

Example: architecto

Sync employee data with LHR.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/dienstnehmer/sync" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"user_id\": \"019f9939-c4c3-70fb-a54e-5ebf63db36d2\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/dienstnehmer/sync"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/dienstnehmer/sync';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'user_id' => '019f9939-c4c3-70fb-a54e-5ebf63db36d2',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/dienstnehmer/sync');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "user_id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/lhr/dienstnehmer/sync

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

user_id   string     

Example: 019f9939-c4c3-70fb-a54e-5ebf63db36d2

List employee documents from LHR.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/documents" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/documents"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/documents');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "documents": []
    }
}
 

Request      

GET api/v1/admin/lhr/users/{userId}/documents

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

userId   string     

Example: architecto

Download payslips from LHR.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/payslip" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/payslip"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/payslip';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/lhr/users/architecto/payslip');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "25d24af1-490a-490e-878a-158c3d0a4b2e",
        "timestamp": "2026-08-21T05:14:36.907967Z"
    }
}
 

Request      

GET api/v1/admin/lhr/users/{userId}/payslip

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

userId   string     

Example: architecto

Admin - Shifts

Admin dashboard endpoints for platform management. Requires admin-level Bearer token authentication.

Shifts

Resolve an attendance hours dispute (conflict) raised during check-out.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/admin/shifts/architecto/resolve-dispute" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"resolution\": \"Stunden laut Anwesenheitsliste bestätigt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/admin/shifts/architecto/resolve-dispute"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "resolution": "Stunden laut Anwesenheitsliste bestätigt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/admin/shifts/architecto/resolve-dispute';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'resolution' => 'Stunden laut Anwesenheitsliste bestätigt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/admin/shifts/architecto/resolve-dispute');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "resolution": "Stunden laut Anwesenheitsliste bestätigt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/admin/shifts/{shiftId}/resolve-dispute

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Body Parameters

resolution   string     

How the dispute was resolved. Example: Stunden laut Anwesenheitsliste bestätigt.

Company - Assignments

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Shifts

List all shift assignments for the organization.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/assignments" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/assignments"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/assignments';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/assignments');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "assignments": [
            {
                "id": "019fd2b8-1636-72ac-b1d9-8d969732067e",
                "status": "signed",
                "status_label": "Unterschrieben",
                "shift_date": "2026-08-25",
                "scheduled_start_time": "08:00:00",
                "scheduled_end_time": "16:00:00",
                "location_name": "MuseumsQuartier Wien",
                "location_address": "Museumsplatz 1, 1070 Wien",
                "hourly_rate_gross": "14.00",
                "confirmed_at": "2026-08-22 00:00",
                "started_at": null,
                "completed_at": null,
                "job": {
                    "id": "019fd2b8-1628-73ae-86e5-b232f0363a96",
                    "title": "Event-Fotograf Assistent",
                    "category": "Events"
                },
                "employee": {
                    "id": "019fd2b8-0d32-71d1-98de-ce0b40a5b75d",
                    "name": "Andreas Weber",
                    "email": "andreas.weber12@demo-employee.at",
                    "city": "Linz"
                }
            },
            {
                "id": "019fd2b8-18c6-71d1-9d10-c4ee02fbff17",
                "status": "signed",
                "status_label": "Unterschrieben",
                "shift_date": "2026-08-07",
                "scheduled_start_time": "08:00:00",
                "scheduled_end_time": "16:00:00",
                "location_name": "H&M Mariahilfer",
                "location_address": "Mariahilfer Straße 42, 1070 Wien",
                "hourly_rate_gross": "13.50",
                "confirmed_at": "2026-08-06 00:00",
                "started_at": null,
                "completed_at": null,
                "job": {
                    "id": "019fd2b8-18bd-713e-8faf-09b99d95cb72",
                    "title": "Kassiere*r Modegeschäft",
                    "category": "Einzelhandel"
                },
                "employee": {
                    "id": "019fd2b8-02bf-72ff-a65c-330126050c95",
                    "name": "Stefan Müller",
                    "email": "stefan.müller0@demo-employee.at",
                    "city": "Wien"
                }
            },
            {
                "id": "019fd2b8-28d3-70d7-92eb-f1bb1577acaf",
                "status": "signed",
                "status_label": "Unterschrieben",
                "shift_date": "2026-08-06",
                "scheduled_start_time": "08:00:00",
                "scheduled_end_time": "16:00:00",
                "location_name": "Nolan-Roberts",
                "location_address": "4309 O'Reilly Way Suite 012",
                "hourly_rate_gross": "34.72",
                "confirmed_at": null,
                "started_at": null,
                "completed_at": null,
                "job": {
                    "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                    "title": "Accountant",
                    "category": "Büro"
                },
                "employee": {
                    "id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "name": "Anna Neuling",
                    "email": "pending@demo.flexxr.at",
                    "city": "Linz"
                }
            },
            {
                "id": "019fd2b8-28d8-728d-a447-8de1a42eec83",
                "status": "checked_in",
                "status_label": "Eingecheckt",
                "shift_date": "2026-08-05",
                "scheduled_start_time": "11:58:00",
                "scheduled_end_time": "12:13:00",
                "location_name": "Goldner and Sons",
                "location_address": "30005 Prosacco Orchard",
                "hourly_rate_gross": "18.57",
                "confirmed_at": null,
                "started_at": null,
                "completed_at": null,
                "job": {
                    "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                    "title": "Accountant",
                    "category": "Büro"
                },
                "employee": {
                    "id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "name": "Anna Neuling",
                    "email": "pending@demo.flexxr.at",
                    "city": "Linz"
                }
            },
            {
                "id": "019fd2b8-28df-7265-8b1e-cb5de026e4d7",
                "status": "completed",
                "status_label": "Abgeschlossen",
                "shift_date": "2026-08-03",
                "scheduled_start_time": "08:31:00",
                "scheduled_end_time": "05:12:00",
                "location_name": "Wehner Inc",
                "location_address": "45222 Hansen Gardens",
                "hourly_rate_gross": "18.25",
                "confirmed_at": null,
                "started_at": null,
                "completed_at": null,
                "job": {
                    "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                    "title": "Accountant",
                    "category": "Büro"
                },
                "employee": {
                    "id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "name": "Anna Neuling",
                    "email": "pending@demo.flexxr.at",
                    "city": "Linz"
                }
            }
        ],
        "summary": {
            "total": 5,
            "by_status": {
                "confirmed": 3,
                "in_progress": 1,
                "completed": 1
            },
            "today": 1,
            "this_week": 3
        }
    },
    "meta": {
        "current_page": 1,
        "last_page": 1,
        "per_page": 20,
        "total": 5
    }
}
 

Request      

GET api/v1/org/shifts/assignments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get the full audit trail for a single assignment (shift).

requires authentication

Returns the assignment summary (incl. contract status + download URL) and a chronological event timeline covering the whole lifecycle: contract signed/countersigned, clock-in/out, completion, hours confirm/dispute, signature refusal, no-show incidents and invoice lines.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/assignments/550e8400-e29b-41d4-a716-446655440000/timeline" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/assignments/550e8400-e29b-41d4-a716-446655440000/timeline"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/assignments/550e8400-e29b-41d4-a716-446655440000/timeline';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/assignments/550e8400-e29b-41d4-a716-446655440000/timeline');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "assignment": {
            "id": "019fd2b8-28d1-7112-b8e9-68bd79fbafb9",
            "status": "awaiting_signature",
            "status_label": "Unterschrift ausstehend",
            "shift_date": "2026-08-04",
            "scheduled_start_time": "16:57:00",
            "scheduled_end_time": "09:21:00",
            "location_name": "Buckridge-Collier",
            "location_address": "661 Cayla Unions Suite 344",
            "hourly_rate_gross": "27.68",
            "started_at": null,
            "completed_at": null,
            "job": {
                "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                "title": "Accountant"
            },
            "employee": {
                "id": "019fd2b7-f271-73fd-8268-848be381e136",
                "name": "Anna Neuling"
            },
            "contract": null,
            "attendance": null,
            "rating": null
        },
        "timeline": [
            {
                "event_type": "assignment.created",
                "actor": "system",
                "title": "Assignment created",
                "description": "Assignment was created from job selection.",
                "occurred_at": "2026-08-05T16:18:32+00:00",
                "path": "/hours"
            }
        ]
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Assignment not found."
}
 

Request      

GET api/v1/org/shifts/assignments/{assignmentId}/timeline

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

assignmentId   string     

The UUID of the shift/assignment. Example: 550e8400-e29b-41d4-a716-446655440000

Company - Attendance

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Shifts

List attendance records for shifts.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/attendance" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/attendance"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/attendance';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/attendance');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "records": [
            {
                "id": "019fd2b8-28d8-728d-a447-8de1a42eec83",
                "shift_date": "2026-08-05",
                "scheduled_start_time": "11:58:00",
                "scheduled_end_time": "12:13:00",
                "status": "checked_in",
                "attendance_status": "checked_in",
                "attendance_status_label": "Eingecheckt",
                "started_at": null,
                "completed_at": null,
                "is_no_show": false,
                "no_show_reason": null,
                "location_name": "Goldner and Sons",
                "job": {
                    "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                    "title": "Accountant"
                },
                "employee": {
                    "id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "name": "Anna Neuling",
                    "email": "pending@demo.flexxr.at",
                    "phone": "+436769876543"
                },
                "contract_signed_at": null,
                "contract_in_force": false,
                "hourly_rate_gross": "18.57"
            }
        ],
        "summary": {
            "total": 1,
            "pending": 0,
            "checked_in": 1,
            "completed": 0,
            "no_show": 0
        }
    },
    "meta": {
        "current_page": 1,
        "last_page": 1,
        "per_page": 50,
        "total": 1
    }
}
 

Request      

GET api/v1/org/shifts/attendance

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Company - Auth

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Authentication

Confirm a company's email ownership by consuming the verification link token (Szenario Unternehmen — "E-Mail-Adresse (Verifizierung)").

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/confirm" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"kontakt@firma.at\",
    \"token\": \"abc123def456...\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/confirm"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "kontakt@firma.at",
    "token": "abc123def456..."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'kontakt@firma.at',
            'token' => 'abc123def456...',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/confirm');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "kontakt@firma.at",
    "token": "abc123def456..."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/auth/verify-email/confirm

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address from the verification link. Example: kontakt@firma.at

token   string     

Verification token from the emailed link. Example: abc123def456...

Resend the company email-verification link. Always returns success to avoid leaking which addresses are registered.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/resend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"kontakt@firma.at\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "kontakt@firma.at"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'kontakt@firma.at',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/auth/verify-email/resend');

  final headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "email": "kontakt@firma.at"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "If this address has a pending verification, a new link has been sent.",
    "data": {
        "email": "kontakt@firma.at"
    }
}
 

Request      

POST api/v1/org/auth/verify-email/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Company email address. Example: kontakt@firma.at

Company - Billing

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Invoices

List invoices for the authenticated organization.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/invoices?status=paid&year=2026&month=5&per_page=20&page=1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/invoices"
);

const params = {
    "status": "paid",
    "year": "2026",
    "month": "5",
    "per_page": "20",
    "page": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/invoices';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'paid',
            'year' => '2026',
            'month' => '5',
            'per_page' => '20',
            'page' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/invoices')
      .replace(queryParameters: {
        'status': 'paid',
        'year': '2026',
        'month': '5',
        'per_page': '20',
        'page': '1',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "invoices": [],
        "summary": {
            "total": 1,
            "paid": 0,
            "pending": 1,
            "overdue": 0,
            "total_paid_cents": 0
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/org/invoices

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

optional Filter by invoice status (draft, generated, sent, paid, overdue, cancelled, partially_paid). Example: paid

year   integer  optional    

optional Filter by billing year. Example: 2026

month   integer  optional    

optional Filter by billing month (1–12). Example: 5

per_page   integer  optional    

optional Items per page (1–100, default 20). Example: 20

page   integer  optional    

optional Page number (default 1). Example: 1

Show a single invoice with line items.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/invoices/550e8400-e29b-41d4-a716-446655440000" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/invoices/550e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/invoices/550e8400-e29b-41d4-a716-446655440000';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/invoices/550e8400-e29b-41d4-a716-446655440000');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "invoice": {
            "id": "3257a732-8d8a-4de5-8dee-a54e85c7e111",
            "invoice_number": "RE-2026-055747",
            "period": "2026-08",
            "status": "draft",
            "status_label": "Draft",
            "reference": null,
            "notes": null,
            "summary": {
                "subtotal_cents": 55952,
                "vat_rate_percent": 20,
                "vat_amount_cents": 11190,
                "platform_fee_cents": 0,
                "total_cents": 67142,
                "paid_amount_cents": 0
            },
            "dates": {
                "invoice_date": "2026-08-05",
                "due_date": "2026-08-19",
                "sent_at": null,
                "paid_at": null
            },
            "pdf_path": null,
            "line_items": []
        }
    }
}
 

Example response (404, Not Found):


{
    "status": "NOT_FOUND",
    "message": "Invoice not found."
}
 

Request      

GET api/v1/org/invoices/{invoiceId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoiceId   string     

The UUID of the invoice. Example: 550e8400-e29b-41d4-a716-446655440000

Company - Jobs

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Jobs

Update a draft job listing for the organization.

requires authentication

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"title\": \"Eventhelfer Messe Wien\",
    \"description\": \"Aufbau und Betreuung eines Messestands.\",
    \"hourly_rate_gross\": \"14.50\",
    \"location_name\": \"Messe Wien\",
    \"location_address\": \"Messeplatz 1\",
    \"location_city\": \"Wien\",
    \"location_postal_code\": \"1020\",
    \"location_lat\": \"48.2208\",
    \"location_lng\": \"16.4108\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "title": "Eventhelfer Messe Wien",
    "description": "Aufbau und Betreuung eines Messestands.",
    "hourly_rate_gross": "14.50",
    "location_name": "Messe Wien",
    "location_address": "Messeplatz 1",
    "location_city": "Wien",
    "location_postal_code": "1020",
    "location_lat": "48.2208",
    "location_lng": "16.4108"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'title' => 'Eventhelfer Messe Wien',
            'description' => 'Aufbau und Betreuung eines Messestands.',
            'hourly_rate_gross' => '14.50',
            'location_name' => 'Messe Wien',
            'location_address' => 'Messeplatz 1',
            'location_city' => 'Wien',
            'location_postal_code' => '1020',
            'location_lat' => '48.2208',
            'location_lng' => '16.4108',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "title": "Eventhelfer Messe Wien",
    "description": "Aufbau und Betreuung eines Messestands.",
    "hourly_rate_gross": "14.50",
    "location_name": "Messe Wien",
    "location_address": "Messeplatz 1",
    "location_city": "Wien",
    "location_postal_code": "1020",
    "location_lat": "48.2208",
    "location_lng": "16.4108"
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The job has been updated successfully.",
    "data": {
        "job": {
            "id": "019fd2b8-6bb5-7113-b62c-7434d5f44733",
            "title": "Lagermitarbeiter Nachtschicht (m/w/d)",
            "description": "Kommissionieren und Verräumen im Zentrallager.",
            "location": {
                "name": "Zentrallager Wien",
                "address": null,
                "city": "Wien",
                "postal_code": null,
                "lat": null,
                "lng": null
            },
            "compensation": {
                "hourly_rate_gross": 15.5,
                "estimated_total_gross": 372
            },
            "timestamps": {
                "updated_at": "2026-08-05 16:18"
            }
        }
    }
}
 

Example response (404, Job Not Found):


{
    "status": "NOT_FOUND",
    "message": "Job not found."
}
 

Example response (422, Job Not Editable):


{
    "status": "INVALID_OPERATION",
    "message": "Only draft jobs can be edited."
}
 

Example response (422, Validation Error):


{
    "status": "ERROR",
    "message": "The title field must not be greater than 200 characters.",
    "errors": {
        "title": [
            "The title field must not be greater than 200 characters."
        ]
    }
}
 

Request      

PUT api/v1/org/jobs/{jobId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

The UUID of the job to update. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

title   string  optional    

optional Updated job title. Max 200 characters. Example: Eventhelfer Messe Wien

description   string  optional    

optional nullable Updated description. Max 5000 characters. Example: Aufbau und Betreuung eines Messestands.

hourly_rate_gross   numeric  optional    

optional Gross hourly rate (minimum 12.00 EUR). Example: 14.50

location_name   string  optional    

optional Location display name. Example: Messe Wien

location_address   string  optional    

optional nullable Street address. Example: Messeplatz 1

location_city   string  optional    

optional nullable City. Example: Wien

location_postal_code   string  optional    

optional nullable Postal code. Example: 1020

location_lat   numeric  optional    

optional nullable Latitude (-90 to 90). Example: 48.2208

location_lng   numeric  optional    

optional nullable Longitude (-180 to 180). Example: 16.4108

Upload the optional cover image for a job listing ("Optional kann ein Bild hochgeladen werden").

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000/cover-image" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "image=@/tmp/phprgl8kashlq3u36M00gl" 
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000/cover-image"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000/cover-image';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'image',
                'contents' => fopen('/tmp/phprgl8kashlq3u36M00gl', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/550e8400-e29b-41d4-a716-446655440000/cover-image');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'multipart/form-data',
    'Accept': 'application/json',
  };

  final request = http.MultipartRequest('POST', uri);
  request.headers.addAll(headers);
  request.files.add(await http.MultipartFile.fromPath('image', '/tmp/phprgl8kashlq3u36M00gl'));

  final streamed = await request.send();
  final response = await http.Response.fromStream(streamed);

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/jobs/{jobId}/cover-image

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

jobId   string     

The UUID of the job. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

image   file     

Job cover image (max 4MB). Example: /tmp/phprgl8kashlq3u36M00gl

Update assignment method and auto-reject settings for a job.

requires authentication

Example request:
curl --request PATCH \
    "https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/assignment-settings" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"assignment_method\": \"manual\",
    \"auto_reject_after_hours\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/assignment-settings"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "assignment_method": "manual",
    "auto_reject_after_hours": 1
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/assignment-settings';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'assignment_method' => 'manual',
            'auto_reject_after_hours' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/jobs/architecto/assignment-settings');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "assignment_method": "manual",
    "auto_reject_after_hours": 1
};

  final response = await http.patch(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "messages.assignment_settings_updated",
    "data": {
        "job_id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
        "assignment_method": "manual",
        "auto_reject_after_hours": 1
    }
}
 

Request      

PATCH api/v1/org/jobs/{jobId}/assignment-settings

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

jobId   string     

Example: architecto

Body Parameters

assignment_method   string     

Allowed: manual, auto, first_come_first_served. Example: manual

auto_reject_after_hours   integer  optional    

Example: 1

Company - No-Show

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Shifts

Request emergency replacement for a no-show incident.

requires authentication

Allowed when the incident status is detected, confirmed, or disputed, and no replacement worker has been assigned yet. Status transitions to replacement_requested.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows/550e8400-e29b-41d4-a716-446655440000/replacement-request" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Dringender Ersatz benötigt - Standup beginnt in 30 Minuten.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows/550e8400-e29b-41d4-a716-446655440000/replacement-request"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Dringender Ersatz benötigt - Standup beginnt in 30 Minuten."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows/550e8400-e29b-41d4-a716-446655440000/replacement-request';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Dringender Ersatz benötigt - Standup beginnt in 30 Minuten.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows/550e8400-e29b-41d4-a716-446655440000/replacement-request');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Dringender Ersatz benötigt - Standup beginnt in 30 Minuten."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Success):


{
    "status": "SUCCESS",
    "message": "Replacement requested.",
    "data": {
        "incident_id": "550e8400-e29b-41d4-a716-446655440000",
        "status": "replacement_requested",
        "status_label": "Ersatz angefordert",
        "replacement_requested_at": "2026-05-10 08:15"
    }
}
 

Example response (404, Incident Not Found):


{
    "status": "NOT_FOUND",
    "message": "No-show incident not found."
}
 

Example response (422, Not Allowed):


{
    "status": "INVALID_OPERATION",
    "message": "Replacement cannot be requested for this incident."
}
 

Request      

POST api/v1/org/shifts/no-shows/{incidentId}/replacement-request

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

incidentId   string     

The UUID of the no-show incident. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

reason   string  optional    

optional nullable Reason for the replacement request (max 500 characters). Example: Dringender Ersatz benötigt - Standup beginnt in 30 Minuten.

Company - No-Shows

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Shifts

List no-show incidents for the organization.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/no-shows');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "incidents": [],
        "summary": {
            "total": 0,
            "unresolved": 0,
            "excused": 0,
            "with_replacement": 0
        }
    },
    "meta": {
        "current_page": 1,
        "last_page": 1,
        "per_page": 20,
        "total": 0
    }
}
 

Request      

GET api/v1/org/shifts/no-shows

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Company - Reports

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Reports

Get workforce reports summary for the organization.

requires authentication

Returns shift statistics, hours metrics, worker engagement, financial overview, no-show analysis, compliance violations, and top workers for the specified date range. Defaults to the current calendar month when no dates are provided.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/reports/summary?date_from=2026-05-01&date_to=2026-05-31" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/reports/summary"
);

const params = {
    "date_from": "2026-05-01",
    "date_to": "2026-05-31",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/reports/summary';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'date_from' => '2026-05-01',
            'date_to' => '2026-05-31',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/reports/summary')
      .replace(queryParameters: {
        'date_from': '2026-05-01',
        'date_to': '2026-05-31',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "period": {
            "date_from": "2026-05-01",
            "date_to": "2026-05-31"
        },
        "kpis": {
            "fill_rate_percent": 0,
            "total_hours": 0,
            "monthly_cost_cents": 0,
            "avg_hours_per_worker": 0,
            "repeat_rate_percent": 0
        },
        "attendance": {
            "pending": 0,
            "checked_in": 0,
            "completed": 0,
            "no_show": 0,
            "total": 0
        },
        "no_shows": {
            "total": 0,
            "excused": 0,
            "unresolved": 0,
            "with_replacement": 0
        },
        "compliance": {
            "score": 100,
            "critical_violations": 0,
            "open_violations": 0,
            "disputed_hours_entries": 0
        }
    }
}
 

Request      

GET api/v1/org/reports/summary

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

date_from   string  optional    

optional Start of the report period (YYYY-MM-DD). Defaults to first day of current month. Example: 2026-05-01

date_to   string  optional    

optional End of the report period (YYYY-MM-DD). Defaults to last day of current month. Example: 2026-05-31

Export compliance report data for the organization.

requires authentication

Returns a structured JSON dataset containing per-shift compliance information (hours worked, breaks, no-show flags, AZG/ARG compliance). Intended for PDF/CSV export by the frontend. Defaults to the current calendar month when no dates are provided.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/reports/compliance/export?date_from=2026-05-01&date_to=2026-05-31" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/reports/compliance/export"
);

const params = {
    "date_from": "2026-05-01",
    "date_to": "2026-05-31",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/reports/compliance/export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'date_from' => '2026-05-01',
            'date_to' => '2026-05-31',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/reports/compliance/export')
      .replace(queryParameters: {
        'date_from': '2026-05-01',
        'date_to': '2026-05-31',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Report export is ready.",
    "data": {
        "filename": "compliance-report-2026-05-01-to-2026-05-31.csv",
        "mime_type": "text/csv; charset=UTF-8",
        "csv": "date,shift_id,job_title,employee_name,employee_email,scheduled_start,scheduled_end,worked_hours,break_minutes,no_show,no_show_status,attendance_dispute,azg_daily_hours_flag\n",
        "rows": 0,
        "period": {
            "date_from": "2026-05-01",
            "date_to": "2026-05-31"
        }
    }
}
 

Request      

GET api/v1/org/reports/compliance/export

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

date_from   string  optional    

optional Start of the export period (YYYY-MM-DD). Defaults to first day of current month. Example: 2026-05-01

date_to   string  optional    

optional End of the export period (YYYY-MM-DD). Defaults to last day of current month. Example: 2026-05-31

Company - Shifts

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Shifts

Resolve step of the smart QR flow: decodes the scanned code and returns the assignment's info + terms + the single currently-valid action (check_in, check_out, or blocked-with-reason). Read-only — never burns the QR's single-use nonce and never mutates state. Whoever scans (worker self-service or shift manager) sees this before confirming.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/resolve" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"qr_payload\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/resolve"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "qr_payload": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/resolve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'qr_payload' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/resolve');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "qr_payload": "architecto"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/scan-qr/resolve

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

qr_payload   string     

The encrypted QR payload from the employee's device. Example: architecto

Confirm step of the smart QR flow: burns the QR's 60-second single-use nonce and executes the freshly re-resolved smart action (check_in or check_out). The action is recomputed here, not trusted from the resolve response, so a state change between the two calls can't be bypassed.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/confirm" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"qr_payload\": \"architecto\",
    \"lat\": 4326.41688,
    \"lng\": 4326.41688,
    \"signature\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "qr_payload": "architecto",
    "lat": 4326.41688,
    "lng": 4326.41688,
    "signature": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'qr_payload' => 'architecto',
            'lat' => 4326.41688,
            'lng' => 4326.41688,
            'signature' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/scan-qr/confirm');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "qr_payload": "architecto",
    "lat": 4326.41688,
    "lng": 4326.41688,
    "signature": "architecto"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/scan-qr/confirm

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

qr_payload   string     

The encrypted QR payload from the employee's device. Example: architecto

lat   number  optional    

Optional GPS latitude of the scanning device. Example: 4326.41688

lng   number  optional    

Optional GPS longitude of the scanning device. Example: 4326.41688

signature   string  optional    

Optional worker signature captured on the device. Example: architecto

Organization reports that an employee refused to sign the shift time record.

requires authentication

Sets dispute_type = 'signature_refused' on the shift attendance.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/signature-refused" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/signature-refused"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/signature-refused';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/signature-refused');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/{shiftId}/signature-refused

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Body Parameters

reason   string  optional    

Example: Krankheitsbedingt kurzfristig abgesagt.

Manager override: the shift manager (or an org admin/owner) checks a worker in directly, no QR needed — how a worker gets checked in past the self-service window. Replaces the old "start the shift" step: check-in IS clock-in, there is no separate start.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/assignments/architecto/check-in" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"lat\": 1,
    \"lng\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/assignments/architecto/check-in"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "lat": 1,
    "lng": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/assignments/architecto/check-in';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'lat' => 1,
            'lng' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/assignments/architecto/check-in');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "lat": 1,
    "lng": 1
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/{shiftId}/assignments/{assignmentId}/check-in

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

assignmentId   string     

Example: architecto

Body Parameters

lat   integer  optional    

Example: 1

lng   integer  optional    

Example: 1

Supervisor ends a worker's in-progress shift early (on-site initiated early checkout — sickness, employer-ended, sent home, …). Records the reason and category and clocks the worker out.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/early-checkout" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Mitarbeiter erkrankt, nach Hause geschickt.\",
    \"category\": \"employer_ended\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/early-checkout"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Mitarbeiter erkrankt, nach Hause geschickt.",
    "category": "employer_ended"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/early-checkout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Mitarbeiter erkrankt, nach Hause geschickt.',
            'category' => 'employer_ended',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/early-checkout');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Mitarbeiter erkrankt, nach Hause geschickt.",
    "category": "employer_ended"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/{shiftId}/early-checkout

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Body Parameters

reason   string     

Why the shift is ending early. Example: Mitarbeiter erkrankt, nach Hause geschickt.

category   string  optional    

Allowed: sickness, injury, misconduct, left_workplace, employer_ended, other. Example: employer_ended

Company modifies an upcoming assigned shift — its scheduled time and/or its location/meeting point — and the assigned worker is notified accordingly ("Schicht geändert" / "Einsatzort geändert").

requires authentication

Only allowed before the shift starts (pending confirmation, confirmed or scheduled).

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/modify" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"shift_date\": \"2026-07-02\",
    \"scheduled_start_time\": \"09:00\",
    \"scheduled_end_time\": \"17:00\",
    \"location_name\": \"Messe Wien Halle B\",
    \"location_address\": \"Messeplatz 1\",
    \"location_lat\": \"48.2208\",
    \"location_lng\": \"16.4108\",
    \"reason\": \"Halle wurde verlegt.\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/modify"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "shift_date": "2026-07-02",
    "scheduled_start_time": "09:00",
    "scheduled_end_time": "17:00",
    "location_name": "Messe Wien Halle B",
    "location_address": "Messeplatz 1",
    "location_lat": "48.2208",
    "location_lng": "16.4108",
    "reason": "Halle wurde verlegt."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/modify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'shift_date' => '2026-07-02',
            'scheduled_start_time' => '09:00',
            'scheduled_end_time' => '17:00',
            'location_name' => 'Messe Wien Halle B',
            'location_address' => 'Messeplatz 1',
            'location_lat' => '48.2208',
            'location_lng' => '16.4108',
            'reason' => 'Halle wurde verlegt.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/9c3f5f3d-0123-4abc-b456-426614174000/modify');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "shift_date": "2026-07-02",
    "scheduled_start_time": "09:00",
    "scheduled_end_time": "17:00",
    "location_name": "Messe Wien Halle B",
    "location_address": "Messeplatz 1",
    "location_lat": "48.2208",
    "location_lng": "16.4108",
    "reason": "Halle wurde verlegt."
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/shifts/{shiftId}/modify

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

UUID of the shift. Example: 9c3f5f3d-0123-4abc-b456-426614174000

Body Parameters

shift_date   date  optional    

New shift date (YYYY-MM-DD). Example: 2026-07-02

scheduled_start_time   string  optional    

New start time (HH:MM). Example: 09:00

scheduled_end_time   string  optional    

New end time (HH:MM). Example: 17:00

location_name   string  optional    

New location display name. Example: Messe Wien Halle B

location_address   string  optional    

New street address. Example: Messeplatz 1

location_lat   numeric  optional    

New latitude. Example: 48.2208

location_lng   numeric  optional    

New longitude. Example: 16.4108

reason   string  optional    

Why the shift is being changed. Example: Halle wurde verlegt.

List shifts pending time confirmation.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/shifts/pending-confirmation" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/pending-confirmation"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/pending-confirmation';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/pending-confirmation');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "shifts": [
            {
                "id": "019fd2b8-28df-7265-8b1e-cb5de026e4d7",
                "job": {
                    "id": "019fd2b8-28c4-732f-a94c-fb5a71a7d682",
                    "title": "Accountant"
                },
                "employee": {
                    "id": "019fd2b7-f271-73fd-8268-848be381e136",
                    "name": "Anna Neuling",
                    "lhr_synced": false
                },
                "date": "2026-08-03",
                "planned_start": "08:31",
                "planned_end": "05:12",
                "actual_check_in": null,
                "actual_check_out": null,
                "worked_minutes": 0,
                "break_minutes": 0,
                "status": "completed",
                "dispute_reason": null,
                "hourly_rate": 18.25,
                "gross_amount": 0
            }
        ],
        "summary": {
            "total_shifts": 1,
            "total_minutes": 0,
            "total_gross": 0
        },
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/org/shifts/pending-confirmation

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Confirm shift worked time.

requires authentication

When a company confirms the worked time, the payout process is triggered via LHR (lohn.at). This is the critical step that initiates employee payment.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/confirm" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"actual_minutes\": 1,
    \"break_minutes\": 1,
    \"note\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "actual_minutes": 1,
    "break_minutes": 1,
    "note": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'actual_minutes' => 1,
            'break_minutes' => 1,
            'note' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/confirm');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "actual_minutes": 1,
    "break_minutes": 1,
    "note": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "Shift time confirmed, payout initiated.",
    "data": {
        "shift_id": "019fd2b8-a5fe-72da-bca5-e12ff2951c28",
        "status": "hours_confirmed",
        "actual_minutes": 480,
        "gross_amount": 263.36,
        "payout_queued": true
    }
}
 

Example response (422, Open requests on the shift):


{
    "status": "INVALID_OPERATION",
    "message": "Diese Schicht hat noch offene Anfragen. Bitte zuerst alle Anfragen entscheiden.",
    "data": {
        "pending_requests": [
            {
                "id": "019f9939-c4c3-70fb-a54e-5ebf63db36d2",
                "type": "overtime",
                "type_label": "Überstunden"
            }
        ]
    }
}
 

Request      

POST api/v1/org/shifts/{shiftId}/confirm

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Body Parameters

actual_minutes   integer  optional    

Example: 1

break_minutes   integer  optional    

Example: 1

note   string  optional    

Example: Beispieltext

Dispute shift worked time.

requires authentication

When a company disputes the worked time, the payout is held pending resolution. The dispute must be resolved before the employee can be paid.

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/dispute" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Krankheitsbedingt kurzfristig abgesagt.\",
    \"disputed_minutes\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/dispute"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "disputed_minutes": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/dispute';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Krankheitsbedingt kurzfristig abgesagt.',
            'disputed_minutes' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/shifts/architecto/dispute');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "reason": "Krankheitsbedingt kurzfristig abgesagt.",
    "disputed_minutes": 1
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "message": "The shift time has been disputed.",
    "data": {
        "shift_id": "019fd2b8-a9b8-7256-a83d-365a8c7761c8",
        "status": "disputed",
        "reason": "Die erfasste Zeit weicht von unserer Aufzeichnung ab."
    }
}
 

Request      

POST api/v1/org/shifts/{shiftId}/dispute

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Body Parameters

reason   string     

Example: Krankheitsbedingt kurzfristig abgesagt.

disputed_minutes   integer  optional    

Example: 1

Company - Wallet

Organization (Company Portal) endpoints for managing jobs, applicants, shifts, team, and organization profiles. Requires organization-level Bearer token. Some endpoints require approved organization status.

Wallet

Returns the authenticated organization's wallet summary, auto-creating the wallet row on first access via {@see WalletService::walletFor()}.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/wallet" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "balance_cents": 250000,
        "blocked_cents": 0,
        "available_cents": 250000,
        "currency": "EUR",
        "has_payment_method": false,
        "withdrawable_cents": 0
    }
}
 

Request      

GET api/v1/org/wallet

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Paginated wallet ledger, newest first. Auto-creates the wallet via {@see WalletService::walletFor()} so a fresh organization sees an empty list instead of a 404.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/wallet/transactions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/transactions"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/transactions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/transactions');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "transactions": [
            {
                "id": "019fd2b8-c69e-714a-8069-e4d9c2c222ac",
                "type": "topup",
                "type_label": "Aufladung",
                "amount_cents": 250000,
                "balance_after_cents": 250000,
                "blocked_after_cents": 0,
                "description": "Guthaben für den Testlauf",
                "reference_type": null,
                "created_at": "2026-08-05T16:19:12+00:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 1
        }
    }
}
 

Request      

GET api/v1/org/wallet/transactions

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Admin-configured top-up amounts with their fee preview, so the portal can render top-up buttons without duplicating the fee formula client-side.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/wallet/topup-options" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup-options"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/topup-options';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/topup-options');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "options": [
            {
                "amount_cents": 100000,
                "platform_fee_cents": 0,
                "stripe_fee_cents": 1525,
                "vat_cents": 0,
                "total_charged_cents": 101525
            },
            {
                "amount_cents": 200000,
                "platform_fee_cents": 0,
                "stripe_fee_cents": 3025,
                "vat_cents": 0,
                "total_charged_cents": 203025
            },
            {
                "amount_cents": 500000,
                "platform_fee_cents": 0,
                "stripe_fee_cents": 7525,
                "vat_cents": 0,
                "total_charged_cents": 507525
            }
        ]
    }
}
 

Request      

GET api/v1/org/wallet/topup-options

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Creates a pending WalletTopup for one of the admin-configured amounts and, when Stripe is configured, opens a hosted Checkout Session for it. The wallet is only credited later — by App\Http\Controllers\StripeWebhookController once `checkout.session.completed` confirms payment.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"amount_cents\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "amount_cents": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/topup';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'amount_cents' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/topup');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "amount_cents": 1
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/wallet/topup

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

amount_cents   integer     

Example: 1

POST /org/wallet/topup/confirm — settle a top-up right on the checkout return instead of waiting for the async webhook. Looks the top-up up by its checkout session id (scoped to the caller's organisation), asks Stripe for the session's real state and settles it if paid. Idempotent: the shared settle path means a later webhook or the reconcile cron can never double-credit.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup/confirm" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"session_id\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "session_id": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/topup/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'session_id' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/topup/confirm');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "session_id": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/wallet/topup/confirm

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

session_id   string     

Example: Beispieltext

POST /org/wallet/topup/cancel — close a top-up the instant the user returns from an abandoned checkout, instead of leaving it pending until the session expires. Safety-first: if Stripe says the session was in fact paid (e.g. the user paid in another tab then hit back), it's settled rather than failed.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup/cancel" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"session_id\": \"Beispieltext\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topup/cancel"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "session_id": "Beispieltext"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/topup/cancel';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'session_id' => 'Beispieltext',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/topup/cancel');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "session_id": "Beispieltext"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/wallet/topup/cancel

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

session_id   string     

Example: Beispieltext

Paginated top-up history, newest first, with the fee breakdown and whether a receipt PDF is available for download.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/wallet/topups" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topups"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/topups';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/topups');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "topups": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/org/wallet/topups

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Streams the receipt PDF for a succeeded top-up. Guarded at the route by the `invoices.download` ACL ability (see App\Providers\AclServiceProvider) on top of the org-member auth/2FA/approved guard, and here by ownership + the topup actually having a stored receipt.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/wallet/topups/architecto/receipt" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/topups/architecto/receipt"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/topups/architecto/receipt';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/topups/architecto/receipt');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (503):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "status": "ERROR",
    "message": "Service Unavailable",
    "data": null,
    "errors": [
        {
            "field": null,
            "message": "Service Unavailable"
        }
    ],
    "meta": {
        "request_id": "cecb0139-a407-44f0-afa8-04001609da18",
        "timestamp": "2026-08-21T05:14:35.364603Z"
    }
}
 

Request      

GET api/v1/org/wallet/topups/{topupId}/receipt

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

topupId   string     

Example: architecto

POST /org/wallet/withdraw — return available wallet balance to the card that funded it (Stripe refund). Amounts up to the auto-approval threshold complete instantly; larger ones come back `pending` for an admin to approve.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/wallet/withdraw" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"amount_cents\": 1
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/withdraw"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "amount_cents": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/withdraw';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'amount_cents' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/withdraw');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "amount_cents": 1
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/wallet/withdraw

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

amount_cents   integer     

Example: 1

GET /org/wallet/withdrawals — the company's withdrawal history, newest first.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "withdrawals": [],
        "pagination": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 20,
            "total": 0
        }
    }
}
 

Request      

GET api/v1/org/wallet/withdrawals

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

POST /org/wallet/withdrawals/{withdrawalId}/cancel — a company withdraws its own still-pending (over-threshold, awaiting-admin-review) withdrawal request before it's reviewed, releasing the reserved funds back to its available balance immediately.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals/architecto/cancel" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals/architecto/cancel"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals/architecto/cancel';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/org/wallet/withdrawals/architecto/cancel');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.post(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/org/wallet/withdrawals/{withdrawalId}/cancel

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

withdrawalId   string     

Example: architecto

Employee - Shifts

Endpoints for the mobile application (employee users). All endpoints (except authentication) require a valid Bearer token with 2FA verification.

Shifts

Download / retrieve a shift time record summary for the authenticated employee.

requires authentication

Returns worked hours, break time, and a gross earnings estimate for the shift.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/time-record" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/time-record"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/time-record';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/shifts/architecto/time-record');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "shift_id": "019fd4e7-ff04-72e4-a010-3ff64e0d3e8f",
        "shift_date": "2026-08-06",
        "scheduled_start_time": "02:30:00",
        "scheduled_end_time": "10:30:00",
        "clock_in_at": "2026-08-06T02:30:01+00:00",
        "clock_out_at": "2026-08-06T10:30:01+00:00",
        "total_hours_worked": 8,
        "break_minutes": 480,
        "net_hours_worked": 0,
        "hourly_rate": 22.64,
        "gross_amount": 0,
        "dispute_type": null
    }
}
 

Request      

GET api/v1/mobile/shifts/{shiftId}/time-record

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

shiftId   string     

Example: architecto

Employee - Wallet

Endpoints for the mobile application (employee users). All endpoints (except authentication) require a valid Bearer token with 2FA verification.

Wallet

Get the authenticated employee's wallet summary.

requires authentication

Aggregates earnings from completed/confirmed shifts, separating pending and paid amounts.

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/wallet" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/wallet"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/wallet';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/wallet');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "total_earned_gross": 0,
        "pending_payout_gross": 0,
        "processing_payout_gross": 0,
        "paid_gross": 0,
        "shifts_completed": 2,
        "shifts_pending_payment": 2,
        "pending_payments": [
            {
                "shift_id": "019fd3d3-f998-72c7-affb-87f0986474d4",
                "shift_date": "2026-08-03",
                "job_title": "Communications Teacher",
                "organization_name": "Caritas",
                "gross_amount": 0,
                "payment_status": "pending",
                "expected_payout_by": "2026-08-17"
            },
            {
                "shift_id": "019fd3d3-f98a-70d2-83d5-eba7cae30f9a",
                "shift_date": "2026-08-06",
                "job_title": "Communications Teacher",
                "organization_name": "Caritas",
                "gross_amount": 0,
                "payment_status": "pending",
                "expected_payout_by": "2026-08-20"
            }
        ],
        "recent_payments": []
    }
}
 

Request      

GET api/v1/mobile/wallet

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Paginated payment history for the authenticated worker — the full ledger of paid shifts, beyond the last-5 preview surfaced on the wallet summary.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/wallet/payments?status=paid&per_page=20&page=1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/wallet/payments"
);

const params = {
    "status": "paid",
    "per_page": "20",
    "page": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/wallet/payments';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'paid',
            'per_page' => '20',
            'page' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/wallet/payments')
      .replace(queryParameters: {
        'status': 'paid',
        'per_page': '20',
        'page': '1',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": [],
    "meta": {
        "current_page": 1,
        "last_page": 1,
        "per_page": 20,
        "total": 0
    }
}
 

Request      

GET api/v1/mobile/wallet/payments

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Filter by payment status: paid|processing|pending. Example: paid

per_page   integer  optional    

Items per page (1-100, default 20). Example: 20

page   integer  optional    

Page number. Example: 1

Stream the authenticated worker's legally binding monthly Lohnzettel (Gehaltszettel) for a given period from lohn.at. Distinct from the per-shift Verdienstabrechnung — this is the official monthly payslip.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/wallet/payslip?period=2026-05" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"period\": \"6425-59\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/wallet/payslip"
);

const params = {
    "period": "2026-05",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "period": "6425-59"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/wallet/payslip';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'period' => '2026-05',
        ],
        'json' => [
            'period' => '6425-59',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/wallet/payslip')
      .replace(queryParameters: {
        'period': '2026-05',
      });

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "period": "6425-59"
};

  final response = await http.get(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Example response (200):


PDF file download
 

Request      

GET api/v1/mobile/wallet/payslip

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

period   string     

The payroll period in YYYY-MM format. Example: 2026-05

Body Parameters

period   string     

Must match the regex /^\d{4}-\d{2}$/. Example: 6425-59

Mobile - Profile

Endpoints for the mobile application (employee users). All endpoints (except authentication) require a valid Bearer token with 2FA verification.

Education

List the authenticated employee's education (Ausbildung) records.

requires authentication

Example request:
curl --request GET \
    --get "https://backend-dev.flexxr.at/api/v1/mobile/education" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/education"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/education';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/education');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.get(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Example response (200, Recorded from a live call):


{
    "status": "SUCCESS",
    "data": {
        "education": []
    }
}
 

Request      

GET api/v1/mobile/education

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Add an education (Ausbildung) record for the authenticated employee.

requires authentication

Example request:
curl --request POST \
    "https://backend-dev.flexxr.at/api/v1/mobile/education" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"institution\": \"HTL Wien\",
    \"degree\": \"Matura\",
    \"field_of_study\": \"Elektrotechnik\",
    \"level\": \"Matura\",
    \"start_year\": 2008,
    \"end_year\": 2013,
    \"is_completed\": true,
    \"document_id\": \"architecto\"
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/education"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "institution": "HTL Wien",
    "degree": "Matura",
    "field_of_study": "Elektrotechnik",
    "level": "Matura",
    "start_year": 2008,
    "end_year": 2013,
    "is_completed": true,
    "document_id": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/education';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'institution' => 'HTL Wien',
            'degree' => 'Matura',
            'field_of_study' => 'Elektrotechnik',
            'level' => 'Matura',
            'start_year' => 2008,
            'end_year' => 2013,
            'is_completed' => true,
            'document_id' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/education');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "institution": "HTL Wien",
    "degree": "Matura",
    "field_of_study": "Elektrotechnik",
    "level": "Matura",
    "start_year": 2008,
    "end_year": 2013,
    "is_completed": true,
    "document_id": "architecto"
};

  final response = await http.post(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

POST api/v1/mobile/education

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

institution   string     

Name of the school/university. Example: HTL Wien

degree   string  optional    

Degree or certificate. Example: Matura

field_of_study   string  optional    

Example: Elektrotechnik

level   string  optional    

Lehre/Matura/Bachelor/Master/... Example: Matura

start_year   integer  optional    

Example: 2008

end_year   integer  optional    

Example: 2013

is_completed   boolean  optional    

Example: true

document_id   string  optional    

Optional linked uploaded document UUID. Example: architecto

Update one of the authenticated employee's education records.

requires authentication

Example request:
curl --request PUT \
    "https://backend-dev.flexxr.at/api/v1/mobile/education/architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"institution\": \"Beispieltext\",
    \"degree\": \"Beispieltext\",
    \"field_of_study\": \"Beispieltext\",
    \"level\": \"Beispieltext\",
    \"start_year\": 1,
    \"end_year\": 1,
    \"is_completed\": true
}"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/education/architecto"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "institution": "Beispieltext",
    "degree": "Beispieltext",
    "field_of_study": "Beispieltext",
    "level": "Beispieltext",
    "start_year": 1,
    "end_year": 1,
    "is_completed": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/education/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'institution' => 'Beispieltext',
            'degree' => 'Beispieltext',
            'field_of_study' => 'Beispieltext',
            'level' => 'Beispieltext',
            'start_year' => 1,
            'end_year' => 1,
            'is_completed' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/education/architecto');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final body = {
    "institution": "Beispieltext",
    "degree": "Beispieltext",
    "field_of_study": "Beispieltext",
    "level": "Beispieltext",
    "start_year": 1,
    "end_year": 1,
    "is_completed": true
};

  final response = await http.put(
    uri,
    headers: headers,
    body: jsonEncode(body),
  );

  print(jsonDecode(response.body));
}

Request      

PUT api/v1/mobile/education/{educationId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

educationId   string     

Example: architecto

Body Parameters

institution   string  optional    

Example: Beispieltext

degree   string  optional    

Example: Beispieltext

field_of_study   string  optional    

Example: Beispieltext

level   string  optional    

Example: Beispieltext

start_year   integer  optional    

Example: 1

end_year   integer  optional    

Example: 1

is_completed   boolean  optional    

Example: true

Delete one of the authenticated employee's education records.

requires authentication

Example request:
curl --request DELETE \
    "https://backend-dev.flexxr.at/api/v1/mobile/education/architecto" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://backend-dev.flexxr.at/api/v1/mobile/education/architecto"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://backend-dev.flexxr.at/api/v1/mobile/education/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final uri = Uri.parse('https://backend-dev.flexxr.at/api/v1/mobile/education/architecto');

  final headers = {
    'Authorization': 'Bearer {YOUR_AUTH_KEY}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final response = await http.delete(
    uri,
    headers: headers,
  );

  print(jsonDecode(response.body));
}

Request      

DELETE api/v1/mobile/education/{educationId}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

educationId   string     

Example: architecto