Projektentwickler

Integrieren Sie Sellio über HTTP oder verbinden Sie Ihren KI-Agenten

Eine einfache REST API und ein nativer MCP-Server, mit demselben Key, demselben Tenant-Scope und demselben Rate Limit. Erkunden Sie die Felder jedes Objekts und beginnen Sie in Minuten mit dem Lesen und Schreiben von Datensätzen.

Schnellstart

Von null zu Ihrem ersten Call in drei Schritten:

  1. Öffnen Sie im CRM Einstellungen → API & Entwickler und generieren Sie einen API-Key. Bewahren Sie ihn sicher auf; er wird nicht erneut angezeigt.
  2. Verwenden Sie den Key im Authorization: Bearer Header. Er identifiziert den zugehörigen Tenant, sodass alles bereits darauf beschränkt ist.
  3. Führen Sie Ihren ersten Call aus und listen Sie Ihre Objekte auf.
curl -H "Authorization: Bearer sk_live_..." \
  https://www.selliocrm.com/api/v1/objects

Authentifizierung

Jede Anfrage trägt einen API-Key im Authorization: Bearer Header. Dieser Key identifiziert den zugehörigen Tenant, sodass alles automatisch darauf beschränkt ist, mit RLS und RBAC. Geben Sie den Key niemals im Browser preis; verwenden Sie ihn nur serverseitig. Sie generieren und widerrufen Keys unter Einstellungen → API & Entwickler.

Key-Scopes

Wenn Sie einen Key generieren, wählen Sie seinen Scope und geben jeder Integration nur den Zugriff, den sie braucht. Die Beschränkungen gelten in REST und MCP gleichermaßen, da beide Oberflächen denselben Key und dieselbe Kontrolle nutzen. Ein Key ohne gesetzten Scope (der Standard) hat vollen Lese- und Schreibzugriff auf alle Objekte, wie bisher.

  • Nur Lesen: Der Key liest Daten, kann aber nicht erstellen, bearbeiten oder löschen. Er blockiert POST, PATCH und DELETE in REST sowie die Tools create_record, update_record und delete_record in MCP, mit 403 und { "error": "errors.apiKeyReadOnly" }.
  • Auf Objekte beschränkt: Wählen Sie die Objekte, auf die der Key zugreifen darf. Jedes Objekt außerhalb der Liste gibt 403 zurück, mit { "error": "errors.apiKeyObjectDenied" }. Die Objektliste kommt bereits auf den Scope gefiltert, und Aktivitäten zählen als das activity-Objekt.
  • Löschen erlauben: Standardmäßig löscht ein Key NIEMALS (nicht einmal mit aktiviertem Schreibzugriff). Aktivieren Sie dies beim Generieren des Keys, um DELETE /{object}/{id} und das Tool delete_record freizuschalten. Ohne diese Option gibt das Löschen 403 zurück, mit { "error": "errors.apiKeyDeleteDenied" }. Das Löschen ist ein Soft-Delete: Datensätze wandern in den Papierkorb und können wiederhergestellt werden.

REST API v1

Datensätze jedes Objekts, Senden und Empfangen von JSON. {object} ist der apiName (zum Beispiel contact, lead, opportunity) und {id} ist die uuid des Datensatzes. Benutzerdefinierte Objekte verwenden dieselben generischen Endpunkte.

GET/objectsDie Tenant-Objekte auflisten.
GET/objects/{apiName}Objektfelder (Discovery).
GET/{object}Datensätze auflisten und durchsuchen (umfangreiche Filter, Sortierung, Paginierung).
GET/{object}/{id}Ein Datensatz per id.
POST/{object}Einen Datensatz erstellen.
POST/{object}/bulkMassenerstellung (bis zu 500; teilweiser Erfolg pro Element).
PATCH/{object}/{id}Felder aktualisieren (teilweise).
PATCH/{object}/bulkMassenaktualisierung ({ updates: [{ id, ...fields }] }).
DELETE/{object}/{id}Löschen (Soft → Papierkorb). Erfordert den delete-Scope auf dem Key.
GET/data/{object}Abgeflachte Datensätze als Zeilen (BI/Automatisierung).
GET/activitiesAktivitäten auflisten (verwenden Sie ?recordId für einen einzelnen Datensatz).
POST/activitiesEine mit einem Datensatz verknüpfte Aktivität erstellen.
GET/line-itemsProduct line-items of an opportunity (?opportunityId), plus the total.
POST/line-itemsAdd a line-item; recomputes totals and the opportunity amount.
PATCH/line-itemsUpdate quantity, price, discount, tax or term of one line.
DELETE/line-itemsRemove a line-item (?id) and recompute the totals.
GET/attachmentsFiles attached to a record (?recordId and ?objectApiName).
POST/attachments/upload-urlUpload step 1: returns the signed upload URL.
POST/attachmentsUpload step 3: registers the uploaded file as an attachment.
GET/attachments/{id}Signed download URL (5 min). There is never a public URL.
DELETE/attachments/{id}Delete the attachment. Requires the delete scope on the key.

Listenparameter (GET /{object}): limit (1 bis 500, Standard 50), offset (Paginierungs-Offset, Standard 0; der Gesamtwert steht im Feld total), search (Suche nach Name, akzent- und groß-/kleinschreibungsunabhängig), order (Feld mit :asc oder :desc) und filter. Umfangreiche Filter: Wiederholen Sie filter=field:operator:value, um mehrere zu kombinieren (z. B. filter=amount:gte:1000&filter=stage:eq:won). Operatoren: eq, neq, contains, gt, lt, gte, lte, empty, not_empty (empty und not_empty nehmen keinen Wert). Filter werden mit logischem AND kombiniert (es gibt kein OR) und bis zu 12 pro Anfrage. Die alte Form filter=field:value (ohne Operator) bedeutet weiterhin exakte Gleichheit.

# Auflisten (limit, offset, search, filter, order)
curl -H "Authorization: Bearer sk_live_..." \
  "https://www.selliocrm.com/api/v1/opportunity?limit=20&order=updated_at:desc&filter=stage:won"

# Umfangreiche Filter: Wiederholen Sie filter=field:operator:value (AND dazwischen)
curl -H "Authorization: Bearer sk_live_..." \
  "https://www.selliocrm.com/api/v1/opportunity?filter=amount:gte:1000&filter=stage:eq:won&filter=name:contains:acme"

# Ein Datensatz
curl -H "Authorization: Bearer sk_live_..." \
  https://www.selliocrm.com/api/v1/contact/8f3c...

# Erstellen
curl -X POST -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Ana Souza","email":"ana@acme.com","source":"site"}' \
  https://www.selliocrm.com/api/v1/contact

# Massenerstellung (bis zu 500; Antwort mit teilweisem Erfolg pro Element)
curl -X POST -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"records":[{"name":"Ana"},{"name":"Bruno"}]}' \
  https://www.selliocrm.com/api/v1/contact/bulk
# → { "results": [ { "index": 0, "ok": true, "id": "..." },
#                  { "index": 1, "ok": false, "error": "..." } ],
#     "created": 1, "failed": 1 }

# Aktualisieren (partiell)
curl -X PATCH -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"source":"referral"}' \
  https://www.selliocrm.com/api/v1/contact/8f3c...

# Löschen (Soft, geht in den Papierkorb; erfordert den delete-Scope auf dem Key)
curl -X DELETE -H "Authorization: Bearer sk_live_..." \
  https://www.selliocrm.com/api/v1/contact/8f3c...

Objekt- und Feld-Discovery

Bevor Sie einen Datensatz erstellen oder aktualisieren, erkunden Sie die Objektfelder: was existiert, was erforderlich ist und welche Werte ein Select akzeptiert. Kein Raten und keine Notwendigkeit, einen bestehenden Datensatz zu untersuchen. GET /objects/{apiName} gibt das Objekt und seine Felder zurück, mit required, options (für select), targetObject (für lookup), unique und readOnly (Formel- oder Rollup-Felder, die nicht beschreibbar sind).

GET https://www.selliocrm.com/api/v1/objects/contact

# → {
#   "apiName": "contact",
#   "label": "Contact",
#   "fields": [
#     { "apiName": "name",  "label": "Name",  "type": "text",  "required": true },
#     { "apiName": "email", "label": "Email", "type": "email", "required": false },
#     { "apiName": "source", "label": "Source", "type": "select", "required": true,
#       "options": [ { "value": "site", "label": "Website" },
#                    { "value": "referral", "label": "Referral" } ] },
#     { "apiName": "company", "label": "Company", "type": "lookup",
#       "required": false, "targetObject": "company" }
#   ]
# }

In MCP gibt das Tool describe_object dieselben Felder an den Agenten zurück.

Daten für BI und Automatisierung

GET /data/{object} gibt Datensätze abgeflacht als stabile tabellarische Zeilen zurück (feste Spalten id, created_at, updated_at, owner_id, plus eine pro Feld), sortiert nach updated_at desc. Es akzeptiert Paginierung per page und pageSize (oder limit und offset), die inkrementellen Filter updated_since und created_since (ISO 8601) sowie filter=field:value. Gebaut für Power BI, Tableau, Looker und für Polling aus Zapier, Make und n8n.

curl -H "Authorization: Bearer sk_live_..." \
  "https://www.selliocrm.com/api/v1/data/opportunity?pageSize=100&updated_since=2026-01-01T00:00:00Z"

# → { "object": "opportunity",
#     "columns": [ { "name": "id", "type": "id" }, ... ],
#     "page": 1, "pageSize": 100, "total": 42,
#     "rows": [ { "id": "...", "created_at": "...", "amount": 1200, ... } ] }

MCP-Server für KI-Agenten

Sellio exposes a native MCP (Model Context Protocol) server so your AI agent can read and write the CRM safely. It is the same API key, the same rate limit, the same validations and the same RBAC. The server URL is https://www.selliocrm.com/api/mcp. Available tools:

  • list_objects: Die Tenant-Objekte auflisten.
  • describe_object: Die Felder eines Objekts (erforderlich, Typen, Optionen).
  • list_records: Datensätze auflisten und durchsuchen.
  • get_record: Ein Datensatz per id.
  • create_record: Einen Datensatz erstellen.
  • update_record: Felder aktualisieren.
  • delete_record: Löschen (Soft → Papierkorb). Erfordert den delete-Scope auf dem Key.

Clients, die Remote-MCP über HTTP unterstützen, verwenden die URL mit dem Authorization: Bearer Header. Bei Clients ohne natives Remote-HTTP verwenden Sie die mcp-remote-Bridge:

{
  "mcpServers": {
    "sellio-crm": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://www.selliocrm.com/api/mcp",
        "--header", "Authorization: Bearer sk_live_..."
      ]
    }
  }
}

OAuth und verbundene Apps

Um IM NAMEN DES NUTZERS zu integrieren (ohne dass der Kunde einen Admin-Key einfügt), verwenden Sie den OAuth 2.0 Authorization Code Flow mit PKCE. Der Nutzer sieht einen Zustimmungsbildschirm mit den angeforderten Scopes, stimmt zu, und Ihre App erhält ein Access Token, das nur das tun kann, was zugestimmt wurde. Apps werden kuratiert: Registrieren und veröffentlichen Sie die App im Marketplace, um eine client_id und ein client_secret zu erhalten. Der Ablauf in vier Schritten:

  1. Leiten Sie den Nutzer an /api/oauth/authorize weiter, mit client_id, redirect_uri (exakte Übereinstimmung mit der App-Allowlist), scope, state und PKCE (code_challenge mit code_challenge_method=S256). PKCE ist erforderlich.
  2. Der angemeldete Nutzer sieht den Zustimmungsbildschirm und stimmt zu. Das CRM leitet zurück an Ihre redirect_uri, mit code und demselben state.
  3. Tauschen Sie auf Ihrem Server den code bei POST /api/oauth/token (mit code_verifier, client_id, client_secret und redirect_uri) gegen ein Access Token (Bearer, etwa 1 Stunde) und ein Refresh Token.
  4. Rufen Sie die REST v1 API und den MCP-Server mit Authorization: Bearer at_... auf, beschränkt auf die Zustimmung. Erneuern Sie mit grant_type=refresh_token; das Refresh Token wird rotiert (das alte verfällt bei Nutzung) und eine Wiederverwendung widerruft die gesamte Token-Familie.
GET/api/oauth/authorizeNutzerzustimmung (erfordert eine Sitzung). Stellt den code aus.
POST/api/oauth/tokencode gegen Tokens tauschen; und erneuern (refresh_token).
POST/api/oauth/revokeEin Access- oder Refresh Token widerrufen.
# 1) Leiten Sie den Nutzer zur Zustimmung (PKCE S256 + state)
GET https://www.selliocrm.com/api/oauth/authorize
  ?response_type=code
  &client_id=app_...
  &redirect_uri=https://your-app.example.com/callback   # EXAKTE Übereinstimmung aus der Allowlist
  &scope=records:read%20records:write:opportunity%20activities:read
  &state=<random>
  &code_challenge=<base64url(sha256(code_verifier))>
  &code_challenge_method=S256

# → der Nutzer stimmt zu → 302 redirect_uri?code=<authcode>&state=<...>

# 2) Tauschen Sie den code gegen Tokens (serverseitig; senden Sie das client_secret)
curl -X POST https://www.selliocrm.com/api/oauth/token \
  -d grant_type=authorization_code \
  -d code=<authcode> \
  -d code_verifier=<code_verifier> \
  -d client_id=app_... \
  -d client_secret=secret_... \
  -d redirect_uri=https://your-app.example.com/callback

# → { "access_token": "at_...", "token_type": "Bearer",
#     "expires_in": 3600, "refresh_token": "rt_...",
#     "scope": "records:read records:write:opportunity activities:read" }

# 3) Rufen Sie die v1- / MCP-API mit dem Access Token auf (auf die Zustimmung beschränkt)
curl -H "Authorization: Bearer at_..." https://www.selliocrm.com/api/v1/contact

# 4) Erneuern Sie mit dem Refresh Token (rotiert: das alte rt_ verfällt bei Nutzung)
curl -X POST https://www.selliocrm.com/api/oauth/token \
  -d grant_type=refresh_token -d refresh_token=rt_... \
  -d client_id=app_... -d client_secret=secret_...

OAuth-Scopes ordnen sich derselben Durchsetzung zu wie Keys (Lesen, Schreiben und pro Objekt). Fordern Sie das Minimum an, das nötig ist; der Nutzer gewährt bei der Installation eine Teilmenge, und das Token arbeitet nur innerhalb dieser.

  • records:read und records:write: Datensätze aller Objekte lesen und schreiben (grob).
  • records:read:contact und records:write:opportunity: Verfeinerung pro Objekt (apiName), wenn die App nur einige Objekte benötigt.
  • activities:read und activities:write: Aktivitäten lesen und schreiben.
  • objects:read: die Struktur von Objekten und Feldern erkunden.

Eingebettete Widgets

Ihre App kann ihren eigenen Bildschirm (ein Widget) im CRM einbetten: in einer Datensatzdetailansicht oder im Dashboard. Das Widget läuft in einem sandboxed iframe, ausgeliefert von Ihrer eigenen Origin (auf der App-Allowlist). Der Host sendet dem Widget nur UI-Kontext über postMessage: recordId, objectApiName, locale und theme. Keine Geschäftsdaten, kein Token und kein Secret werden in dieser Nachricht übertragen. Um Daten zu lesen oder zu schreiben, verwendet Ihr Widget die OAuth API mit ihrem eigenen Token (den Scopes, die der Tenant bei der Installation gewährt hat).

  1. Deklarieren Sie das Widget in Ihrer App (key, title, location record oder dashboard und die embedUrl) und listen Sie die Origin der embedUrl auf der App-Origin-Allowlist auf.
  2. Hören Sie in Ihrem Widget auf Host-Nachrichten und akzeptieren Sie nur jene von der Host-Origin (event.origin). Der Host sendet sellio:widget:context (Version 1) mit { recordId, objectApiName, locale, theme, installId }.
  3. Ihr Widget darf nur sellio:widget:ready (um den Kontext erneut zu empfangen) und sellio:widget:resize mit { height } (um seine Höhe anzupassen, begrenzt) zurücksenden. Jede andere Nachricht wird ignoriert.
  4. Für Daten rufen Sie die OAuth API (Bearer) mit dem App-Token auf. Der Host übergibt niemals ein Token oder Daten über postMessage.
// Innerhalb IHRES Widgets (die Seite unter https://widgets.yourapp.com, die im iframe läuft).
// Der Host sendet NUR UI-Kontext. Für DATEN verwenden Sie die OAuth API mit IHREM Token.
const HOST = 'https://www.selliocrm.com'; // Validieren Sie IMMER die Host-Origin

window.addEventListener('message', (event) => {
  if (event.origin !== HOST) return;            // akzeptieren Sie nur die Host-Origin
  const msg = event.data;
  if (!msg || msg.version !== 1) return;
  if (msg.type === 'sellio:widget:context') {
    const { recordId, objectApiName, locale, theme, installId } = msg.payload;
    render(recordId, objectApiName, locale, theme);
    // Rufen Sie Daten mit IHREM OAuth-Token ab (kommt nie über postMessage):
    // fetch(HOST + '/api/v1/' + objectApiName + '/' + recordId,
    //       { headers: { Authorization: 'Bearer ' + accessToken } })
  }
});

// Fordern Sie den Kontext beim Laden an und passen Sie die Höhe an Ihren Inhalt an:
parent.postMessage({ type: 'sellio:widget:ready', version: 1 }, HOST);
parent.postMessage({ type: 'sellio:widget:resize', version: 1,
  payload: { height: document.body.scrollHeight } }, HOST);

Sicherheit: Das iframe ist sandboxed und cross-origin, sodass das Widget das Host-DOM oder die Host-Cookies nicht erreichen kann. Der Host validiert die exakte Origin bei jedem postMessage (ein- und ausgehend), und die embedUrl wird immer gegen die App-Allowlist geprüft. Fordern Sie den Scope widgets:embed an, um Widgets zu platzieren.

Ausgehende Webhooks

Empfangen Sie CRM-Ereignisse in Echtzeit. Registrieren Sie eine Ziel-URL in den Einstellungen und wählen Sie die Ereignisse, die Sie abonnieren. Jede Zustellung ist ein POST mit einem JSON-Body, dem Header x-sellio-event mit dem Ereignisnamen und dem Header x-sellio-signature mit der HMAC-SHA256-Signatur des Bodys. Das Secret (whsec_...) wird angezeigt, wenn Sie den Webhook erstellen.

Ereigniskatalog

record.createdrecord.updatedflow.enrolledflow.message_sentflow.repliedflow.step_completedflow.completed

Beispiel-Payload

POST https://your-server.example.com/webhook
x-sellio-event: record.created
x-sellio-signature: sha256=<hmac-hex>
Content-Type: application/json

{
  "event": "record.created",
  "tenantId": "…",
  "objectApiName": "lead",
  "recordId": "8f3c…",
  "data": { "name": "Ana Souza", "email": "ana@acme.com" },
  "timestamp": "2026-07-23T12:00:00.000Z"
}

Kampagnen- und Flow-Ereignisse (flow.*) tragen zusätzliche Enrollment-Felder:

{
  "event": "flow.replied",
  "tenantId": "…",
  "flowId": "…",
  "flowName": "Outbound Q3",
  "recordId": "8f3c…",
  "enrollmentId": "…",
  "objectApiName": "lead",
  "data": { "name": "Ana Souza", "email": "ana@acme.com" },
  "meta": {},
  "timestamp": "2026-07-23T12:00:00.000Z"
}

So verifizieren Sie die Signatur

Berechnen Sie den HMAC-SHA256 des ROHEN Bodys (genau wie empfangen, ohne erneute Serialisierung) mit Ihrem whsec_...-Secret, in der Form sha256=<hex>, und vergleichen Sie ihn in konstanter Zeit mit dem Header x-sellio-signature. Weisen Sie ab, wenn er nicht übereinstimmt.

import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody = der EXAKT empfangene Body (String), ohne erneute Serialisierung.
function isValid(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(signatureHeader || '');
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

// Express: verwenden Sie express.raw({ type: 'application/json' }), um den rohen Body zu erhalten.
app.post('/webhook', (req, res) => {
  const ok = isValid(req.body.toString('utf8'), req.header('x-sellio-signature'), process.env.SELLIO_WHSEC);
  if (!ok) return res.status(401).end();
  const event = req.header('x-sellio-event');
  // ... das Ereignis verarbeiten
  res.status(200).end();
});

Rate Limit

Jeder Key hat ein Limit pro Minute (Standard 120, konfigurierbar). Antworten enthalten die Header X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset. Bei Überschreitung gibt die API 429 mit dem Header Retry-After zurück.

Fehlerformat

Fehler geben JSON in der Form { "error": "message" } mit dem passenden HTTP-Status zurück:

{ "error": "required field: source" }   # HTTP 400
  • 200 und 201: Erfolg (201 beim Erstellen).
  • 400: Validierung oder Geschäftsregel.
  • 401: fehlender oder ungültiger Key.
  • 403: keine Berechtigung (RBAC) für das Objekt, oder durch den Key-Scope blockiert (nur Lesen, Objekt außerhalb des Scopes oder Löschen nicht aktiviert).
  • 404: Objekt oder Datensatz nicht gefunden (einschließlich DELETE/PATCH einer nicht existierenden id).
  • 429: Rate Limit überschritten (siehe den Header Retry-After).

Bekannte Einschränkungen

  • Filter werden mit logischem AND kombiniert (alle gleichzeitig wahr); es gibt kein OR zwischen Filtern.
  • Aktualisierungen sind partiell (PATCH): Sie senden nur die zu ändernden Felder. Es gibt kein PUT zum vollständigen Ersetzen eines Datensatzes.

Spezifikation und Collection

Importieren Sie die OpenAPI 3.1-Spezifikation in Swagger, Insomnia oder Postman, oder laden Sie die fertige Postman-Collection mit jedem Endpunkt und den Variablen apiKey und baseUrl herunter.

Erstellen Sie Ihr Konto und generieren Sie den ersten Key

Kostenloses Konto erstellen
Projektentwickler · Sellio