Quote API · v1

three91 Quote API

Mit der API holt ein Programm (ERP, OmniFab, eigener Import) die Anfragen ab, die in three91 Quote ausgelesen und übernommen wurden: Kopfdaten, Positionen mit Material, Dicke, Menge und Maßen und die CAD-Datei jeder Position.

Basis-URLhttps://api.quote.three91.com
Testumgebung: https://api.quote.stage.three91.com
FormatJSON (UTF-8), Zeiten in ISO 8601 mit Zeitzone, Längen in mm, Gewichte in kg
Zugriffnur lesend, ein API-Key pro Programm, ausschließlich über HTTPS

Die API liefert nur, was im Unternehmen übernommen wurde. Anfragen, die noch geprüft werden, sowie abgelehnte oder entfernte Positionen erscheinen nicht.

API-Key

Einen Key erzeugt ein Administrator in three91 Quote unter Einstellungen → Unternehmen → API-Keys. Der Key wird nur ein einziges Mal angezeigt. Er gehört zu genau einem Unternehmen und sieht nur dessen Daten. Dort sieht man auch, wann ein Key zuletzt benutzt wurde, und kann ihn widerrufen.

Das Programm schickt den Key bei jedem Aufruf im Header Authorization mit:

Authorization: Bearer t91_live_…

Behandeln Sie den Key wie ein Passwort: nicht in Quelltext oder Logdateien, nicht per E-Mail. Für jedes anbindende Programm sollte es einen eigenen Key geben, dann lässt sich jeder einzeln widerrufen.

GET /v1/ping

Prüft den Key. Antwortet mit dem Unternehmen, zu dem er gehört.

curl -H "Authorization: Bearer $THREE91_KEY" https://api.quote.three91.com/v1/ping
{ "ok": true, "tenant_id": "ca2e241e-90ab-4b02-8bc5-4580fa596040" }

GET /v1/requests

Übernommene Anfragen mit allen Positionen, sortiert nach der letzten Änderung, die älteste zuerst, seitenweise.

ParameterBedeutung
updated_sinceoptional, ISO 8601 (z. B. 2026-11-10T08:00:00Z): nur Anfragen, die zu diesem Zeitpunkt oder danach übernommen oder geändert wurden
cursoroptional, der Wert next_cursor der vorigen Seite
limitoptional, Anfragen pro Seite, 1–100, Standard 20
statusoptional, derzeit nur accepted (Standard)
curl -H "Authorization: Bearer $THREE91_KEY" \
  "https://api.quote.three91.com/v1/requests?updated_since=2026-11-10T08:00:00Z&limit=20"
{
  "data": [ { …Anfrage… }, { …Anfrage… } ],
  "next_cursor": "MjAyNi0xMS0xMFQwODoxNToy…"
}

Ist next_cursor nicht null, gibt es weitere Seiten: denselben Aufruf mit cursor=<next_cursor> wiederholen, bis next_cursor null ist.

GET /v1/requests/{id}

Eine einzelne Anfrage, gleiches Format wie ein Eintrag in data. 404, wenn es sie nicht gibt, sie (noch) nicht übernommen wurde oder archiviert ist.

GET /v1/requests/{id}/positions/{position_id}/geometry

Die CAD-Datei der Position (DXF oder STEP) als Download. Die Adresse steht fertig in geometry_file.url; Name, Typ und Größe ebenfalls dort. Andere Dateien der Anfrage (E-Mail, Excel, PDF-Zeichnungen) liefert die API nicht aus.

curl -H "Authorization: Bearer $THREE91_KEY" -o AT-1201.dxf \
  https://api.quote.three91.com/v1/requests/c60cee13-…/positions/b11809bb-…/geometry

Gibt es mehrere Dateien zu einem Teil, wählt die API in dieser Reihenfolge: eine DXF des Kunden, dann die STEP des Einzelteils (aus einer Baugruppe herausgetrennt), dann die STEP, wie sie kam, zuletzt eine von three91 abgeleitete DXF (z. B. aus einer PDF-Zeichnung).

Positionen ohne CAD-Datei haben geometry_file: null. Ist das Teil ein einfacher Zuschnitt, steht stattdessen die Form in shape (Rechteck, Kreis oder Ring), damit es sich als Makro anlegen lässt.

Neue Daten abholen

Die API arbeitet per Abruf. Das anbindende Programm braucht nur ausgehende HTTPS-Verbindungen und kann z. B. alle 5 Minuten fragen:

  1. Merken Sie sich den größten updated_at-Wert der zuletzt verarbeiteten Anfrage (am Anfang leer).
  2. GET /v1/requests?updated_since=<dieser Wert> aufrufen und alle Seiten über next_cursor abholen.
  3. Jede Anfrage über ihre id anlegen oder aktualisieren. Eine Anfrage kommt erneut, wenn sie oder eine ihrer Positionen in three91 geändert wurde.
  4. Den neuen größten updated_at-Wert speichern.

Weil updated_since den Zeitpunkt selbst einschließt, kommt die zuletzt verarbeitete Anfrage einmal doppelt. Der Import sollte daher über id (Anfrage) und positions[].id (Position) erkennen, was er schon hat.

Feld-Referenz: Anfrage

FeldTypBedeutung
idUUIDfeste Kennung der Anfrage in three91
referenceTextBetreff der Anfrage-Mail
customer_referenceText | nullAnfrage- oder Bestellnummer des Kunden
statusTextaccepted
languageText | nullSprache der Anfrage (z. B. de)
received_at · accepted_at · updated_atZeiteingegangen · übernommen · letzte Änderung (Anfrage oder eine Position)
customerObjekt | nullaus Absender und Signatur gelesen: company, contact_name, contact_role, contact_email, phone, street, postal_code, city, country, vat_id, homepage. Eine Kundennummer aus Ihrem System kennt three91 nicht. Zuordnen lässt sich der Kunde über Firma, USt-IdNr. oder E-Mail.
delivery_deadline · response_deadlineObjektdate (ISO-Datum oder null) und text (wie in der Anfrage, z. B. „KW 48“)
delivery_address · payment_terms · incotermsText | nullwenn in der Anfrage genannt
operationsListeArbeitsgänge aus dem Anfragetext: code, scope (all = alle Positionen, items = einzelne), text (Originalwortlaut), confidence
notesListe von TextenHinweise zur ganzen Anfrage
positionsListedie Positionen, siehe unten

Feld-Referenz: Position

FeldTypBedeutung
idUUIDfeste Kennung der Position
position_noZahllaufende Nummer ab 1
item_no · drawing_no · article_no · revisionText | nullPositions-, Zeichnungs-, Artikelnummer und Revision, wie in der Anfrage (oft nicht numerisch, z. B. AT-1201)
part_nameTextTeilename, sonst Zeichnungs- oder Positionsnummer; immer gefüllt
quantityZahl | nullStückzahl
materialObjekttext (z. B. S235JR, normalisiert) und category (mild_steel, structural, high_strength, wear_plate, pressure_vessel, alloy_steel, stainless, aluminum …)
thickness_mmZahl | nullBlechdicke
width_mm · length_mmZahl | nullAußenmaße (umschließendes Rechteck)
shapeObjekt | nullnur ohne CAD-Datei: {type: "rectangle", width_mm, length_mm}, {type: "circle", diameter_mm} oder {type: "ring", outer_diameter_mm, inner_diameter_mm}
cutting_length_mmZahl | nullSchnittlänge aller Konturen
net_area_mm2 · surface_area_m2Zahl | nullNetto-Fläche
weight_per_piece_kg · weight_total_kgZahl | nullGewicht je Stück und gesamt
bevelsListeFasen: type (V, Y, X, K), angle_deg, depth_mm, length_mm
operationsListeArbeitsgänge für diese Position: code, text, from
notesListeHinweise zur Position: text, category
geometry_fileObjekt | nullname, type (dxf, step, stp), size_bytes, url (relativ zur Basis-URL)
statusTextBearbeitungsstand in three91 (z. B. identified, priced)
qualityObjektcompleteness (0–1), flags (offene Prüfhinweise), sources (woher Material, Dicke, Menge, Maße stammen, z. B. xlsx_csv, dxf_analysis), edited_by_user (von Hand korrigierte Felder)

Jeder Wert steht direkt im Feld. Wie sicher er ist, steht getrennt in quality; ein einfacher Import kann quality ignorieren.

Fehler & Grenzen

StatusBedeutung
400Parameter ungültig, z. B. updated_since kein ISO-Datum oder cursor beschädigt
401kein Key, falscher Key oder widerrufener Key
404Anfrage, Position oder Datei nicht vorhanden (oder nicht für diesen Key)
429zu viele Aufrufe: höchstens 5 pro Sekunde und Key, kurze Spitzen bis 20. Nach der im Header Retry-After genannten Zahl von Sekunden erneut versuchen.
5xxStörung bei three91; später erneut versuchen

Fehler kommen als JSON: { "detail": "…" }.

Beispiel einer Anfrage

{
  "id": "c60cee13-7145-4484-8a09-68357c0babaf",
  "reference": "Brennteile AT-1200 – please quote",
  "customer_reference": "AT-1200",
  "status": "accepted",
  "language": "de",
  "received_at": "2026-11-02T15:38:45.094887+00:00",
  "accepted_at": "2026-11-02T18:20:19.468397+00:00",
  "updated_at": "2026-11-02T18:20:19.468397+00:00",
  "customer": {
    "company": "Huber Anlagenbau GmbH", "contact_name": "Stefan Huber",
    "contact_email": "s.huber@huber-anlagen.example", "city": "Linz", "country": "AT",
    "contact_role": null, "phone": null, "street": null, "postal_code": null,
    "vat_id": null, "homepage": null
  },
  "delivery_deadline": { "date": null, "text": "KW 48" },
  "response_deadline": { "date": null, "text": null },
  "delivery_address": null, "payment_terms": null, "incoterms": null,
  "operations": [ { "code": "deburr", "scope": "all", "text": "alle Teile entgratet", "confidence": 0.9 } ],
  "notes": [],
  "positions": [
    {
      "id": "b11809bb-1cfd-4834-9d08-9a2d600172f2",
      "position_no": 1,
      "item_no": "AT-1201", "part_name": "AT-1201", "drawing_no": "AT-1201",
      "article_no": null, "revision": null,
      "quantity": 8,
      "material": { "text": "S235JR", "category": "mild_steel" },
      "thickness_mm": 12,
      "width_mm": 400, "length_mm": 60,
      "shape": null,
      "cutting_length_mm": 920,
      "net_area_mm2": 24000, "surface_area_m2": 0.024,
      "weight_per_piece_kg": 2.26, "weight_total_kg": 18.08,
      "bevels": [],
      "operations": [ { "code": "deburr", "text": "alle Teile entgratet", "from": "request" } ],
      "notes": [],
      "geometry_file": {
        "name": "AT-1201.dxf", "type": "dxf", "size_bytes": 14425,
        "url": "/v1/requests/c60cee13-7145-4484-8a09-68357c0babaf/positions/b11809bb-1cfd-4834-9d08-9a2d600172f2/geometry"
      },
      "status": "identified",
      "quality": {
        "completeness": 0.8, "flags": [],
        "sources": { "material": "xlsx_csv", "thickness_mm": "xlsx_csv", "quantity": "xlsx_csv", "width_mm": "dxf_analysis" },
        "edited_by_user": []
      }
    }
  ]
}

Versionierung

Die Version steht im Pfad (/v1). Innerhalb von v1 kommen höchstens neue Felder hinzu; bestehende Felder werden nicht umbenannt, entfernt oder in ihrer Bedeutung geändert. Ein Import sollte unbekannte Felder daher ignorieren. Was sich nicht so ändern lässt, kommt als /v2, und v1 bleibt dann mit Vorlauf erhalten.

Fragen zur Anbindung: info@three91.com