REST API v1
Integriere time2invoice mit deiner Buchhaltungssoftware, Automatisierungs-Tools oder eigenen Anwendungen. Die API gibt dir vollen Zugriff auf Rechnungen, Kunden, Zeiterfassung und EÜR-Berichte.
https://good-roadrunner-663.convex.site/api/v1/Authentifizierung
Alle API-Anfragen müssen mit einem API-Key authentifiziert werden. Erstelle einen Key unter Einstellungen → API-Zugang.
Sende den Key im Authorization-Header:
curl -H "Authorization: Bearer t2i_deinApiKey..." \
https://good-roadrunner-663.convex.site/api/v1/invoicesconst response = await fetch("https://good-roadrunner-663.convex.site/api/v1/invoices", {
headers: {
"Authorization": "Bearer t2i_deinApiKey...",
"Content-Type": "application/json"
}
});
const { data, meta, error } = await response.json();⚠️ Sicherheitshinweis: API-Keys haben vollen Zugriff auf deinen Account. Teile sie niemals öffentlich und verwende sie nur in Server-seitigem Code, niemals im Frontend.
Rate Limiting
Die API erlaubt 100 Anfragen pro Minute pro API-Key. Bei Überschreitung erhältst du einen 429-Status.
| Header | Beschreibung |
|---|---|
| X-RateLimit-Limit | Maximale Anfragen pro Fenster (100) |
| X-RateLimit-Remaining | Verbleibende Anfragen |
| Retry-After | Sekunden bis zum nächsten Fenster |
Response-Format
Alle Antworten folgen einem konsistenten JSON-Format:
{
"data": { ... },
"meta": { "total": 42 },
"error": null
}{
"data": null,
"meta": null,
"error": "Invalid or expired API key. Pro plan required."
}Invoices (Rechnungen)
/api/v1/invoicesAlle Rechnungen auflisten. Optional nach Status filtern.
Query-Parameter
statusstring— "draft" | "sent" | "paid"Response
{
"data": [
{
"_id": "jd7x...",
"invoiceNumber": "FR-2026-001",
"customerId": "k3f9...",
"invoiceDate": 1735689600000,
"netAmount": 2400,
"status": "sent",
"activityType": "freiberuflich",
"taxRate": 0,
"taxAmount": 0,
"grossAmount": 2400,
"createdAt": 1735689600000
}
],
"meta": { "total": 1 },
"error": null
}/api/v1/invoicesNeue Rechnung erstellen (Status: draft).
Request Body (JSON)
customerIdstringrequired— Kunden-IDinvoiceNumberstringrequired— z.B. "FR-2026-002"invoiceDatenumberrequired— Unix-Timestamp (ms)periodStartnumberrequired— Leistungszeitraum StartperiodEndnumberrequired— Leistungszeitraum EndenetAmountnumberrequired— Nettobetrag in Euronotesstring— AnmerkungentaxRatenumber— USt-Satz (0 = Kleinunternehmer)taxAmountnumber— USt-BetraggrossAmountnumber— BruttobetragactivityTypestring— "freiberuflich" | "gewerblich"Response
{
"data": { "id": "jd7x..." },
"meta": { "created": true },
"error": null
}Customers (Kunden)
/api/v1/customersAlle aktiven Kunden auflisten.
Response
{
"data": [
{
"_id": "k3f9...",
"name": "Acme GmbH",
"email": "info@acme.de",
"address": "Musterstr. 1, 10115 Berlin",
"hourlyRate": 90,
"isActive": true,
"activityType": "freiberuflich"
}
],
"meta": { "total": 1 },
"error": null
}/api/v1/customersNeuen Kunden erstellen.
Request Body (JSON)
namestringrequired— KundennamehourlyRatenumberrequired— Stundensatz in Euroemailstring— E-Mail-Adresseaddressstring— AnschrifttaxNumberstring— Steuernummer des KundenustIdstring— USt-IdNr. des KundenactivityTypestring— "freiberuflich" | "gewerblich"Response
{
"data": { "id": "k3f9..." },
"meta": { "created": true },
"error": null
}Time Entries (Zeiteinträge)
/api/v1/time-entriesZeiteinträge auflisten. Optional nach Zeitraum filtern.
Query-Parameter
fromstring— Startdatum (ISO 8601, z.B. 2026-01-01)tostring— Enddatum (ISO 8601)Response
{
"data": [
{
"_id": "te42...",
"projectId": "p1x7...",
"customerId": "k3f9...",
"startTime": 1735689600000,
"endTime": 1735700400000,
"durationMinutes": 180,
"description": "UI-Entwicklung Login-Page",
"isRunning": false
}
],
"meta": { "total": 1 },
"error": null
}/api/v1/time-entriesNeuen Zeiteintrag erstellen (abgeschlossen, kein laufender Timer).
Request Body (JSON)
projectIdstringrequired— Projekt-IDcustomerIdstringrequired— Kunden-IDstartTimenumberrequired— Startzeit (Unix-Timestamp ms)endTimenumberrequired— Endzeit (Unix-Timestamp ms)descriptionstring— Beschreibung der TätigkeitResponse
{
"data": { "id": "te42..." },
"meta": { "created": true },
"error": null
}Projects (Projekte)
/api/v1/projectsAlle aktiven Projekte auflisten.
Response
{
"data": [
{
"_id": "p1x7...",
"name": "Website Redesign",
"customerId": "k3f9...",
"color": "#3b82f6",
"isActive": true,
"activityType": "freiberuflich"
}
],
"meta": { "total": 1 },
"error": null
}Reports (Berichte)
/api/v1/reports/eurEÜR-Daten (Einnahme-Überschuss-Rechnung) für ein Jahr abrufen. Getrennt nach freiberuflich/gewerblich.
Query-Parameter
yearnumber— Jahr (z.B. 2026). Default: aktuelles JahrResponse
{
"data": {
"year": 2026,
"income": {
"total": 48000,
"byType": {
"freiberuflich": 36000,
"gewerblich": 12000
},
"invoiceCount": 24
},
"expenses": {
"total": 8500,
"count": 42
},
"profit": 39500
},
"meta": null,
"error": null
}Webhooks
Registriere Webhook-Endpoints unter Einstellungen → API-Zugang um Echtzeit-Benachrichtigungen zu erhalten.
Verfügbare Events
| Event | Beschreibung | Auslöser |
|---|---|---|
| invoice.created | Neue Rechnung erstellt | Rechnung wird als Draft angelegt |
| invoice.sent | Rechnung versendet | Status wechselt zu 'sent' |
| invoice.paid | Zahlung eingegangen | Status wechselt zu 'paid' |
| customer.created | Neuer Kunde angelegt | Kunde wird erstellt |
| expense.created | Neue Ausgabe erfasst | Ausgabe wird erstellt |
Payload-Format
POST https://dein-server.de/webhook
Content-Type: application/json
X-T2I-Event: invoice.created
X-T2I-Signature: a1b2c3d4e5f6...
{
"event": "invoice.created",
"timestamp": "2026-01-15T10:30:00.000Z",
"data": {
"id": "jd7x...",
"invoiceNumber": "FR-2026-003",
"netAmount": 1800
}
}Signatur-Verifizierung
Jeder Webhook-Request enthält einen X-T2I-Signature-Header mit einer HMAC-SHA256 Signatur des Request-Bodys, erstellt mit deinem Webhook-Secret.
import crypto from "crypto";
function verifyWebhookSignature(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body, "utf-8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
}
// In deinem Webhook-Handler:
app.post("/webhook", (req, res) => {
const signature = req.headers["x-t2i-signature"];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
"whsec_deinSecret..."
);
if (!isValid) {
return res.status(401).send("Invalid signature");
}
const { event, data } = req.body;
console.log(`Event: ${event}`, data);
res.status(200).send("OK");
});Fehlerbehandlung
| Status | Bedeutung | Lösung |
|---|---|---|
| 400 | Ungültige Anfrage | Prüfe die Request-Parameter und den JSON-Body |
| 401 | Nicht authentifiziert | Authorization-Header mit gültigem API-Key senden |
| 403 | Zugriff verweigert | API-Key ist abgelaufen oder Pro-Plan erforderlich |
| 429 | Rate Limit überschritten | Warte und versuche es erneut (siehe Retry-After Header) |
| 500 | Serverfehler | Versuche es erneut. Bei Wiederholung: Support kontaktieren |
🚀 Quick Start
Komplett-Beispiel: Alle Rechnungen abrufen und den Gesamtumsatz berechnen.
const API_KEY = "t2i_deinApiKey...";
const BASE = "https://good-roadrunner-663.convex.site/api/v1";
async function getTotalRevenue() {
const res = await fetch(`${BASE}/invoices?status=paid`, {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
const { data, error } = await res.json();
if (error) throw new Error(error);
const total = data.reduce((sum, inv) => sum + inv.netAmount, 0);
console.log(`Gesamtumsatz (bezahlt): ${total.toFixed(2)} €`);
console.log(`Anzahl Rechnungen: ${data.length}`);
return total;
}
getTotalRevenue();Fragen zur API? Schreib uns an support@time2invoice.de