CrewIO Developer Portal

Baue produktive Integrationen auf CrewIO

Für SaaS-Builder, Agenturen, Vereine und Plattform-Teams: sichere API-Keys, starke Scopes, Webhooks und ein Endpunkt-Set für vollständige Event-Workflows.

Auth

Bearer oder x-crewio-api-key

crw_live_<keyId>_<secret>

Isolation

Pro Key ein Verein

Kein Cross-Verein Zugriff. Fremde IDs liefern 403.

Rate Limit

Globales API-Limit

100 req/min

Backoff + Retry wird empfohlen.

Webhooks

HMAC-SHA256 + Delivery Logs

HTTPS-only, Signaturprüfung und manuelle Test-Dispatches im Dashboard.

Onboarding in 5 Schritten

1. Integration anlegen

Im Vereins-Dashboard unter Einstellungen > Integrationen eine neue Integration erstellen und die passenden Scopes auswählen.

2. API-Key erzeugen

Einen Key mit Ablaufdatum anlegen und sofort sicher speichern (wird nur einmal angezeigt).

3. Erste Read-Calls

Mit GET /events und GET /events/{eventId}/roles beginnen, um Datenmodell und Verein-Grenzen zu verifizieren.

4. Write-Flow aufsetzen

Danach gezielt Write-Endpunkte einführen (z. B. Assignments oder Availability) und auf Idempotenz achten.

5. Webhooks aktivieren

Webhook registrieren, Signatur validieren, dann mit /webhooks/test eine End-to-End-Lieferung testen.

Sofort starten: API-Calls

Nutze die Sprache, die zu deinem Stack passt.

cURL

curl -X GET 'https://crewio.co/api/developer/v1/events?status=active&limit=20' \
  -H 'Authorization: Bearer crw_live_...'

JavaScript / TypeScript

const res = await fetch('https://crewio.co/api/developer/v1/events', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer crw_live_...'
  }
})

if (!res.ok) {
  const err = await res.json()
  throw new Error(err.error || 'API request failed')
}

const data = await res.json()
console.log(data.events)

Python

import requests

url = 'https://crewio.co/api/developer/v1/events'
headers = {'Authorization': 'Bearer crw_live_...'}
params = {'status': 'active', 'limit': 20}

resp = requests.get(url, headers=headers, params=params, timeout=15)
resp.raise_for_status()

payload = resp.json()
print(payload['events'])

In eigene Apps einbinden

Produktionsmuster für Web, Mobile und SaaS-Backends: CrewIO API serverseitig kapseln und sauber in den App-Flow integrieren.

API-Key nie im Frontend

Mobile/Web Clients sprechen nur mit deinem eigenen Backend. Der CrewIO-Key bleibt ausschließlich serverseitig.

Backend-for-Frontend (BFF)

Erstelle app-spezifische Endpunkte in deiner App und mappe sie auf CrewIO-Requests. So kontrollierst du Rechte und Payloads.

Webhook + Queue

Webhook-Events verarbeiten, sofort 2xx zurückgeben und schwere Jobs asynchron in einer Queue ausführen.

Server-Client (TypeScript)

Ein wiederverwendbarer CrewIO Client für dein Backend.

type CrewioRequestOptions = {
  method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'
  body?: unknown
  query?: Record<string, string | number | boolean | undefined>
}

export class CrewioClient {
  constructor(
    private readonly apiKey: string,
    private readonly baseUrl = 'https://crewio.co/api/developer/v1'
  ) {}

  private buildUrl(path: string, query?: CrewioRequestOptions['query']) {
    const url = new URL(path, this.baseUrl + '/')
    if (query) {
      for (const [key, value] of Object.entries(query)) {
        if (value === undefined) continue
        url.searchParams.set(key, String(value))
      }
    }
    return url.toString()
  }

  async request<T>(path: string, options: CrewioRequestOptions = {}): Promise<T> {
    const res = await fetch(this.buildUrl(path, options.query), {
      method: options.method ?? 'GET',
      headers: {
        'Authorization': 'Bearer ' + this.apiKey,
        'Content-Type': 'application/json',
      },
      body: options.body ? JSON.stringify(options.body) : undefined,
      cache: 'no-store',
    })

    const json = await res.json().catch(() => ({}))
    if (!res.ok) {
      throw new Error(String(json?.error || 'CrewIO API error (' + res.status + ')'))
    }
    return json as T
  }

  listEvents(status?: 'draft' | 'active' | 'archived' | 'cancelled') {
    return this.request<{ events: Array<{ id: string; name: string }> }>('events', {
      query: { status, limit: 50 },
    })
  }

  createShift(eventId: string, payload: {
    role_id: string
    start_time: string
    end_time: string
    required_count?: number
    allow_overbooking?: boolean
    notes?: string | null
  }) {
    return this.request<{ shift: { id: string } }>('events/' + eventId + '/shifts', {
      method: 'POST',
      body: payload,
    })
  }
}

Next.js BFF Route

Frontend ruft nur deinen internen Endpunkt auf.

// app/api/app/events/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { CrewioClient } from '@/lib/crewio-client'

export const dynamic = 'force-dynamic'

const client = new CrewioClient(process.env.CREWIO_API_KEY!)

export async function GET(request: NextRequest) {
  try {
    const status = request.nextUrl.searchParams.get('status') || undefined
    const data = await client.listEvents(
      status as 'draft' | 'active' | 'archived' | 'cancelled' | undefined
    )
    return NextResponse.json(data, { status: 200 })
  } catch (error) {
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      { status: 502 }
    )
  }
}

React Query Hook

Typische Client-Einbindung für eigene Dashboards.

import { useQuery } from '@tanstack/react-query'

type EventLite = { id: string; name: string }

export function useAppEvents() {
  return useQuery({
    queryKey: ['app-events'],
    queryFn: async () => {
      const res = await fetch('/api/app/events?status=active', { credentials: 'include' })
      const json = await res.json()
      if (!res.ok) throw new Error(json?.error || 'Failed to load events')
      return json.events as EventLite[]
    },
  })
}

OpenAPI für SDK-Generierung

Automatisch Clients für TS/Python erstellen.

# OpenAPI laden
curl -fsSL 'https://developers.crewio.co/api/developer/v1/openapi' -o crewio-openapi.json

# TypeScript SDK generieren
npx @openapitools/openapi-generator-cli generate \
  -i crewio-openapi.json \
  -g typescript-fetch \
  -o src/lib/crewio-sdk

# Python SDK generieren
openapi-generator generate \
  -i crewio-openapi.json \
  -g python \
  -o ./crewio_python_client

Scopes für jede App-Größe

Starte klein mit Read-Scopes und erweitere erst bei Bedarf auf Write/Delete.

events:readevents:writeevents:createevents:deleteroles:readroles:writeshifts:readshifts:writehelpers:readhelpers:writemessages:readmessages:writeassignments:readassignments:writeavailability:readavailability:write

Integrations-Blueprints

Control Panel Integration

Next.js + PostgreSQL + Queue Worker + Webhooks

Eigene Backoffice-App zur Event-Steuerung mit Write-Scopes. Perfekt für Agenturen und zentrale Event-Teams.

Data Warehouse Connector

Python + Airflow + BigQuery/ClickHouse

Regelmäßige Reads für BI/KPIs (Besetzung, Response-Raten, Kommunikationsvolumen).

Realtime Operations Bot

Webhook Worker + Redis + Messaging APIs

Verarbeitet Webhooks und triggert externe Aktionen in Slack, Teams, WhatsApp oder Incident Tools.

No-Code Automation Layer

n8n / Make + secure secrets + retry policy

Zapier/Make/n8n Flows über Webhooks als Trigger und API-Endpunkte als Actions.

Vollständige Endpoint-Referenz

Alle Endpunkte sind scoped und verein-isoliert.

Events

MethodPfadScopeBeschreibung
GET/api/developer/v1/eventsevents:readEvents listen (Filter, Limit).
POST/api/developer/v1/eventsevents:createNeues Event anlegen.
GET/api/developer/v1/events/{eventId}events:readEin Event lesen.
PATCH/api/developer/v1/events/{eventId}events:writeEvent aktualisieren.
DELETE/api/developer/v1/events/{eventId}events:deleteEvent löschen.

Rollen & Schichten

MethodPfadScopeBeschreibung
GET/api/developer/v1/events/{eventId}/rolesroles:readRollen laden.
POST/api/developer/v1/events/{eventId}/rolesroles:writeRolle erstellen.
PATCH/api/developer/v1/events/{eventId}/roles/{roleId}roles:writeRolle ändern.
DELETE/api/developer/v1/events/{eventId}/roles/{roleId}roles:writeRolle löschen.
GET/api/developer/v1/events/{eventId}/shiftsshifts:readSchichten laden.
POST/api/developer/v1/events/{eventId}/shiftsshifts:writeSchicht erstellen.
PATCH/api/developer/v1/events/{eventId}/shifts/{shiftId}shifts:writeSchicht ändern.
DELETE/api/developer/v1/events/{eventId}/shifts/{shiftId}shifts:writeSchicht löschen.

Helfer & Nachrichten

MethodPfadScopeBeschreibung
GET/api/developer/v1/events/{eventId}/helpershelpers:readEvent-Helfer lesen.
POST/api/developer/v1/events/{eventId}/helpershelpers:writeHelferzugriff vergeben.
DELETE/api/developer/v1/events/{eventId}/helpers?userId={userId}helpers:writeHelferzugriff entziehen.
GET/api/developer/v1/events/{eventId}/messagesmessages:readNachrichten lesen.
POST/api/developer/v1/events/{eventId}/messagesmessages:writeNachricht erstellen.
PATCH/api/developer/v1/events/{eventId}/messages/{messageId}messages:writeNachricht ändern.
DELETE/api/developer/v1/events/{eventId}/messages/{messageId}messages:writeNachricht löschen.

Assignments & Availability

MethodPfadScopeBeschreibung
GET/api/developer/v1/events/{eventId}/assignmentsassignments:readAssignment-Liste laden.
POST/api/developer/v1/events/{eventId}/assignmentsassignments:writeAssignment upserten.
GET/api/developer/v1/events/{eventId}/availability/requestsavailability:readAvailability-Requests lesen.
POST/api/developer/v1/events/{eventId}/availability/requestsavailability:writeAvailability-Request erstellen.
GET/api/developer/v1/events/{eventId}/availability/responsesavailability:readResponses lesen.
POST/api/developer/v1/events/{eventId}/availability/responsesavailability:writeResponses upserten.

Reale End-to-End Workflows

Copy-Paste-Flows basierend auf den produktiven v1 Endpunkten.

Event Setup

Event to Role to Shift

# 1) Event anlegen
curl -X POST 'https://crewio.co/api/developer/v1/events' \
  -H 'Authorization: Bearer crw_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Sommerfest 2026",
    "start_date": "2026-07-12",
    "end_date": "2026-07-13",
    "status": "active",
    "location": "Stadtpark Mainz"
  }'

# Response: { "event": { "id": "event_uuid", ... } }

# 2) Rolle anlegen
curl -X POST 'https://crewio.co/api/developer/v1/events/event_uuid/roles' \
  -H 'Authorization: Bearer crw_live_...' \
  -H 'Content-Type: application/json' \
  -d '{ "name": "Getränkestand", "color": "#2563EB" }'

# 3) Schicht anlegen
curl -X POST 'https://crewio.co/api/developer/v1/events/event_uuid/shifts' \
  -H 'Authorization: Bearer crw_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "role_id": "role_uuid",
    "start_time": "2026-07-12T14:00:00.000Z",
    "end_time": "2026-07-12T18:00:00.000Z",
    "required_count": 3
  }'

Assignment Sync

Shift + User status upserten

# Assignment upsert (erstellt neu oder aktualisiert bestehenden Datensatz)
curl -X POST 'https://crewio.co/api/developer/v1/events/event_uuid/assignments' \
  -H 'Authorization: Bearer crw_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "shift_id": "shift_uuid",
    "user_id": "user_uuid",
    "state": "confirmed",
    "notes": "Von externer Einsatzplanung übernommen"
  }'

# Beispiel-Response:
# {
#   "assignment": {
#     "id": "assignment_uuid",
#     "shift_id": "shift_uuid",
#     "user_id": "user_uuid",
#     "state": "confirmed"
#   }
# }

Availability Flow

Schichten anfragen und Antworten speichern

# 1) Anfrage mit Zeitfenstern erstellen
curl -X POST 'https://crewio.co/api/developer/v1/events/event_uuid/availability/requests' \
  -H 'Authorization: Bearer crw_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "time_slots": [
      { "id": "slot_a", "label": "Sa 10:00-12:00" },
      { "id": "slot_b", "label": "Sa 12:00-14:00" }
    ]
  }'

# 2) Antworten eines Helfers upserten
curl -X POST 'https://crewio.co/api/developer/v1/events/event_uuid/availability/responses' \
  -H 'Authorization: Bearer crw_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "request_id": "request_uuid",
    "user_id": "user_uuid",
    "responses": [
      { "time_slot_id": "slot_a", "available": true },
      { "time_slot_id": "slot_b", "available": false }
    ]
  }'

Webhook Integration Guide

Events

Subscriptions pro Integration auswählbar

event.created
event.updated
event.deleted
role.created
role.updated
role.deleted
shift.created
shift.updated
shift.deleted
helper.granted
helper.revoked
message.created
message.updated
message.deleted
assignment.updated
availability.request.created
availability.response.upserted

Payload-Shape

Jedes Event enthält type, occurred_at und data.

{
  "id": "evt_123...",
  "type": "assignment.updated",
  "occurred_at": "2026-03-13T15:35:10.000Z",
  "data": {
    "integration_id": "...",
    "event_id": "...",
    "assignment": {
      "id": "...",
      "shift_id": "...",
      "user_id": "...",
      "state": "checked_in"
    }
  }
}

Node.js Signaturprüfung

import crypto from 'crypto'

export function verifyCrewioSignature(rawBody, signatureHeader, signingSecret) {
  const provided = (signatureHeader || '').trim().replace(/^sha256=/i, '')
  if (!/^[0-9a-f]{64}$/i.test(provided)) return false

  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(rawBody, 'utf8')
    .digest('hex')

  const a = Buffer.from(provided, 'hex')
  const b = Buffer.from(expected, 'hex')
  if (a.length !== b.length) return false

  return crypto.timingSafeEqual(a, b)
}

Python Signaturprüfung

import hmac
import hashlib


def verify_crewio_signature(raw_body: bytes, signature_header: str, signing_secret: str) -> bool:
    provided = (signature_header or '').replace('sha256=', '').strip().lower()
    if len(provided) != 64:
        return False

    expected = hmac.new(
        signing_secret.encode('utf-8'),
        raw_body,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(provided, expected)

Production Webhook Handler (Node.js)

Signatur + Timestamp + Replay-Schutz + Async-Verarbeitung

import express from 'express'
import crypto from 'crypto'

const app = express()
app.use('/webhooks/crewio', express.raw({ type: 'application/json' }))

const seen = new Map<string, number>()
const FIVE_MINUTES_MS = 5 * 60 * 1000

function verifySignature(rawBody: Buffer, signatureHeader: string, secret: string) {
  const provided = (signatureHeader || '').replace(/^sha256=/i, '').trim()
  if (!/^[a-f0-9]{64}$/i.test(provided)) return false

  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(provided, 'hex'), Buffer.from(expected, 'hex'))
}

app.post('/webhooks/crewio', (req, res) => {
  const rawBody = req.body as Buffer
  const signature = String(req.header('x-crewio-signature') || '')
  const timestamp = String(req.header('x-crewio-timestamp') || '')

  if (!verifySignature(rawBody, signature, process.env.CREWIO_WEBHOOK_SECRET!)) {
    return res.status(401).json({ error: 'Invalid signature' })
  }

  const ageMs = Math.abs(Date.now() - new Date(timestamp).getTime())
  if (!timestamp || Number.isNaN(ageMs) || ageMs > FIVE_MINUTES_MS) {
    return res.status(400).json({ error: 'Stale timestamp' })
  }

  const payload = JSON.parse(rawBody.toString('utf8')) as { id: string; type: string }
  if (seen.has(payload.id)) {
    return res.status(200).json({ ok: true, duplicate: true })
  }
  seen.set(payload.id, Date.now())

  // Job in Queue legen (BullMQ/SQS/Rabbit), nicht im Request blockieren.
  console.log('CrewIO event', payload.type, payload.id)
  return res.status(200).json({ ok: true })
})

Fehlerbehandlung in Produktion

StatusBedeutungEmpfohlene Reaktion
400Payload/Params ungültigFelder gegen API-Validierung prüfen, Datums-/UUID-Format korrigieren.
401Key fehlt/ungültig/abgelaufen/revokedToken rotieren, Ablaufdatum/Revocation prüfen.
403Scope fehlt oder Verein-FremdzugriffScopes der Integration erweitern und event/verein-Zuordnung checken.
404Ressource existiert nichtID prüfen und sicherstellen, dass sie zum Ziel-Event gehört.
429Rate limit erreichtExponential Backoff + Retry-Jitter + Queueing verwenden.
500Server/DB-FehlerRetry mit Circuit Breaker; Fehler korrelieren und Support kontaktieren.

Nächste Schritte für dein Team

  • Integration mit minimalen Scopes starten (Least Privilege).
  • Webhook-Signaturprüfung + Replay-Schutz serverseitig einbauen.
  • Write-Flows erst nach erfolgreichen Read/Observe-Tests aktivieren.
  • Retries mit Backoff + Dead Letter Queue für robuste Automationen einsetzen.