Démarrage rapide avec l’API
À la fin de cette page, vous aurez échangé une clé d’API contre un jeton porteur, identifié l’organisation et l’équipe dans lesquelles votre identifiant d’accès peut agir, créé une fiche, puis relu celle-ci avec son historique d’événements.
Les deux parcours ci-dessous sont complets et indépendants : chaque élément est défini avant d’être utilisé, et aucun parcours n’emprunte une étape à l’autre. Choisissez un onglet et restez-y.
Avant de commencer
Section intitulée « Avant de commencer »Vous avez besoin des éléments suivants :
- Une clé d’API de compte de service, émise par un administrateur de l’organisation. Les clés d’API personnelles n’existent pas — consultez Authentification et clés d’API si vous n’en avez pas encore.
- Le compte de service doit être membre de l’équipe dans laquelle vous écrivez. La création de fiches nécessite une appartenance actuelle à l’équipe sélectionnée ; un identifiant d’accès au niveau de l’organisation qui n’appartient à aucune équipe peut lire
/api/v1/me, mais ne peut pas créer de fiches. Demandez à votre administrateur de l’ajouter à une équipe si l’étape 3 renvoie403 FORBIDDEN. - Parcours curl :
curletjq(les exemples l’utilisent pour analyser le JSON ; si vous préférez ne pas installerjq, copiez manuellement les valeurs depuis les réponses). - Parcours TypeScript : un environnement d’exécution qui exécute directement TypeScript et dispose d’un
fetchglobal — Node.js 22.18 ou version ultérieure, Bun ou Deno. Sous Node.js 18 ou 20, exécutez le fichier avec un chargeur tel quetsx. Aucun paquet à installer : les exemples utilisent uniquementfetchsans bibliothèque supplémentaire. Un client typé est disponible séparément ; consultez Client TypeScript.
Toutes les requêtes sont envoyées à https://apid.dustid.io ; consultez Environnements pour connaître les autres URL de service.
-
Configurer votre environnement
Section intitulée « Configurer votre environnement »Fenêtre de terminal export APID_URL="https://apid.dustid.io"export DUST_API_KEY="your-service-account-key" # read this from your secrets managerEnregistrez le fichier ci-dessous sous
quickstart.ts, puis exécutez-le avecnode quickstart.ts,bun quickstart.tsoudeno run --allow-net --allow-env quickstart.ts. Chaque étape ajoute du contenu au même fichier.quickstart.ts // Makes the file an ES module, which is what lets the top-level `await`s// below run. (A `.mts` extension, or "type": "module" in package.json,// does the same job.)export {};const apidUrl = "https://apid.dustid.io";const apiKey = process.env.DUST_API_KEY;if (!apiKey) throw new Error("Set DUST_API_KEY in the environment."); -
Échanger la clé d’API contre un jeton porteur
Section intitulée « Échanger la clé d’API contre un jeton porteur »Les clés d’API ne sont jamais envoyées aux points de terminaison
/api/v1/*. Échangez une fois la clé auprès deGET /api/auth/token, en la transmettant dans l’en-têtex-api-key, puis envoyez le JWT obtenu sous la formeAuthorization: Bearer <token>lors de chaque appel suivant.Fenêtre de terminal curl -fsS "$APID_URL/api/auth/token" -H "x-api-key: $DUST_API_KEY"{ "token": "eyJhbGciOi...", "expiresIn": 900, "expiresAt": "2026-09-20T22:40:00.000Z" }Fenêtre de terminal export DUST_TOKEN="$(curl -fsS "$APID_URL/api/auth/token" -H "x-api-key: $DUST_API_KEY" | jq -r '.token')"quickstart.ts type TokenResponse = { token: string; expiresIn?: number; expiresAt?: string };async function exchangeToken(): Promise<TokenResponse> {const response = await fetch(`${apidUrl}/api/auth/token`, {headers: { "x-api-key": apiKey! },});if (!response.ok) {throw new Error(`Token exchange failed: ${response.status} ${await response.text()}`);}return (await response.json()) as TokenResponse;}const { token, expiresIn, expiresAt } = await exchangeToken();console.log(`Token valid for ${expiresIn ?? "unknown"}s (until ${expiresAt ?? "unknown"})`);La réponse contient
tokenet, lorsque le JWT lui-même comporte une déclaration d’expiration,expiresIn(secondes restantes) etexpiresAt(ISO 8601). Lisez la durée de vie dans la réponse au lieu de la coder en dur : les jetons ont actuellement une courte durée de vie (environ 15 minutes) et il n’existe aucun jeton d’actualisation. Une tâche de longue durée doit donc effectuer un nouvel échange en cours d’exécution. Le contrat complet relatif à la durée de vie, une implémentation de mise en cache et le modèle consistant à actualiser une seule fois après un401sont présentés dans Authentification → Expiration et actualisation du jeton. -
Identifier votre organisation
Section intitulée « Identifier votre organisation »GET /api/v1/meest l’un des rares points de terminaison qui ne nécessite aucun en-tête de contexte. Il décrit l’identifiant d’accès lui-même : le principal, les organisations auxquelles il appartient et celle qui est active.Fenêtre de terminal curl -fsS "$APID_URL/api/v1/me" -H "Authorization: Bearer $DUST_TOKEN"{"userId": "6a1f…","email": "sap-connector@example.com","name": "SAP Connector","activeOrganizationId": "b2c7…","organizations": [{ "id": "b2c7…", "name": "Anchor Electronics", "slug": "anchor-electronics", "roles": ["member"] }]}Fenêtre de terminal # Prefer the active organization; fall back to the first membership.export DUST_ORG_ID="$(curl -fsS "$APID_URL/api/v1/me" -H "Authorization: Bearer $DUST_TOKEN" \| jq -er '.activeOrganizationId // .organizations[0].id')"echo "Organization: $DUST_ORG_ID"quickstart.ts type Organization = { id: string; name: string; slug: string; roles: string[] };type MeResponse = {userId: string;email: string;activeOrganizationId?: string | null;organizations: Organization[];};const auth = { Authorization: `Bearer ${token}` };const meResponse = await fetch(`${apidUrl}/api/v1/me`, { headers: auth });if (!meResponse.ok) {throw new Error(`/me failed: ${meResponse.status} ${await meResponse.text()}`);}const me = (await meResponse.json()) as MeResponse;const organizationId =me.activeOrganizationId ?? me.organizations[0]?.id;if (!organizationId) {throw new Error("This credential belongs to no organization — ask your admin.");}console.log(`Organization: ${organizationId}`); -
Choisir une équipe (facultatif)
Section intitulée « Choisir une équipe (facultatif) »Les fiches appartiennent à une équipe au sein de l’organisation. Deux options sont prises en charge :
- Ne rien faire. Omettez
Dust-Ctx-Team-Idet l’API agit dans l’équipe racine de l’organisation. Pour une organisation ne comptant qu’une seule équipe, cela constitue l’intégralité de l’étape 4, et les exemples de l’étape 5 suivent ce parcours. - Indiquer une équipe.
GET /api/v1/teamsrépertorie les équipes dont votre identifiant d’accès est membre, sous la forme{ "teams": [ … ], "total": n }, chacune comportantteamId,orgIdetname. Envoyez l’équipe souhaitée dansDust-Ctx-Team-Id.
Fenêtre de terminal curl -fsS "$APID_URL/api/v1/teams?pageSize=50" \-H "Authorization: Bearer $DUST_TOKEN" \-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \| jq '.teams[] | { teamId, name }'{ "teamId": "b2c7…", "name": "Anchor Electronics" }{ "teamId": "4e90…", "name": "Line 3 Receiving" }Fenêtre de terminal # Optional. Leave DUST_TEAM_ID unset to use the organization's root Team.export DUST_TEAM_ID="4e90…"Chaque requête ci-dessous transmet
-H "Dust-Ctx-Team-Id: ${DUST_TEAM_ID:-}". Une valeur vide est traitée exactement comme un en-tête absent — l’équipe racine de l’organisation — de sorte que le même script fonctionne que vous ayez ou non défini la variable.quickstart.ts type Team = { teamId: string; orgId: string; name: string | null };const teamsResponse = await fetch(`${apidUrl}/api/v1/teams?pageSize=50`, {headers: { ...auth, "Dust-Ctx-Org-Id": organizationId },});if (!teamsResponse.ok) {throw new Error(`/teams failed: ${teamsResponse.status} ${await teamsResponse.text()}`);}const { teams } = (await teamsResponse.json()) as { teams: Team[]; total: number };for (const team of teams) console.log(`${team.teamId} ${team.name ?? "(unnamed)"}`);// Optional. Leave DUST_TEAM_ID unset to act in the organization's root Team.const teamId = process.env.DUST_TEAM_ID;// Context headers for every call from here on. The Team header is present// only when a Team was chosen — an undefined value must not be sent.const context: Record<string, string> = {...auth,"Dust-Ctx-Org-Id": organizationId,...(teamId ? { "Dust-Ctx-Team-Id": teamId } : {}),}; - Ne rien faire. Omettez
-
Créer une fiche
Section intitulée « Créer une fiche »Une fiche constitue l’enregistrement d’un actif ou d’un élément. Une requête
POST /api/v1/threadsavectype: "single"en crée une ;thread.nameest le seul champ obligatoire, et le tableau facultatifdatacontient les champs typés.Fenêtre de terminal curl -fsS "$APID_URL/api/v1/threads" \-H "Authorization: Bearer $DUST_TOKEN" \-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \-H "Dust-Ctx-Team-Id: ${DUST_TEAM_ID:-}" \-H "Content-Type: application/json" \-d '{"type": "single","thread": {"name": "Tire SZ3J-11-ZJ17","description": "Production asset"},"data": [{ "name": "Serial Number", "type": "text", "value": { "text": "SZ3J-11-ZJ17" } },{ "name": "Max PSI", "type": "number", "value": { "number": 51 } }]}' | tee /tmp/created.json | jq '.created[0] | { threadId, name }'{ "threadId": "0f13c0de-2f1a-4a2e-9f60-6d2f7b9f0a11", "name": "Tire SZ3J-11-ZJ17" }Fenêtre de terminal export THREAD_ID="$(jq -r '.created[0].threadId' /tmp/created.json)"quickstart.ts type ThreadRecord = { threadId: string; name: string | null };const createResponse = await fetch(`${apidUrl}/api/v1/threads`, {method: "POST",headers: { ...context, "Content-Type": "application/json" },body: JSON.stringify({type: "single",thread: { name: "Tire SZ3J-11-ZJ17", description: "Production asset" },data: [{ name: "Serial Number", type: "text", value: { text: "SZ3J-11-ZJ17" } },{ name: "Max PSI", type: "number", value: { number: 51 } },],}),});if (!createResponse.ok) {const error = await createResponse.json();throw new Error(`create failed: ${error.code} — ${error.message}`);}const { created } = (await createResponse.json()) as { created: ThreadRecord[] };const threadId = created[0]?.threadId;if (!threadId) throw new Error("The server created no Thread.");console.log(`Created ${threadId}`);Le statut est
201 Createdet le corps est{ "created": [ … ], "uploadResponses": [] }— une structure par lots, car le même point de terminaison crée plusieurs fiches à la fois avectype: "list"outype: "raw". Chaque entrée decreatedest une fiche complète, comprenant sonthreadIdgénéré.Les entrées de champ nécessitent
typeetvalue, et la structure devaluedépend du type :{ "text": "…" }pourtext,{ "number": 51 }pournumber.nameest le libellé du champ. La liste complète des types de champs figure dans le guide des fiches. -
La relire
Section intitulée « La relire »GET /api/v1/threads/{thread_id}renvoie la fiche ainsi que son historique d’événements — chaque écriture est consignée, de sorte que la piste d’audit commence dès la création.Fenêtre de terminal curl -fsS "$APID_URL/api/v1/threads/$THREAD_ID" \-H "Authorization: Bearer $DUST_TOKEN" \-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \-H "Dust-Ctx-Team-Id: ${DUST_TEAM_ID:-}" \| jq '{ name: .thread.name, fields: [.thread.data[]?.name], events: (.events | length) }'{ "name": "Tire SZ3J-11-ZJ17", "fields": ["Serial Number", "Max PSI"], "events": 1 }quickstart.ts const getResponse = await fetch(`${apidUrl}/api/v1/threads/${threadId}`, {headers: context,});if (!getResponse.ok) {const error = await getResponse.json();throw new Error(`read failed: ${error.code} — ${error.message}`);}const record = (await getResponse.json()) as {thread: { name: string | null };events: unknown[];};console.log(record.thread.name); // "Tire SZ3J-11-ZJ17"console.log(record.events.length); // at least 1 — creation is an eventLa structure de la réponse est
{ "thread": { … }, "events": [ … ] }. Le nombre exact d’événements ne fait pas partie du contrat — attendez-vous à en recevoir au moins un.
En cas d’échec
Section intitulée « En cas d’échec »| Ce que vous avez obtenu | Ce que cela signifie |
|---|---|
401 UNAUTHORIZED lors de l’échange | La clé d’API est incorrecte, révoquée ou n’est pas une clé de compte de service. Les clés personnelles ne permettent pas de s’authentifier. |
401 UNAUTHORIZED lors d’un appel /api/v1/* | Le jeton porteur a expiré (ces jetons ont une courte durée de vie). Effectuez un nouvel échange et réessayez une fois. |
400 ORG_ID_REQUIRED | Vous avez omis Dust-Ctx-Org-Id sur un point de terminaison limité à une organisation. |
400 INVALID_REQUEST mentionnant un en-tête | Un en-tête de contexte n’était pas un UUID. Les en-têtes sont validés avant l’exécution du point de terminaison. |
403 FORBIDDEN lors de la création | Le compte de service n’est pas membre de l’équipe sélectionnée. Demandez à votre administrateur de l’y ajouter. |
404 lors de la relecture | Il s’agit généralement d’un contexte incorrect, et non d’une fiche manquante — une fiche n’est visible que dans l’organisation et l’équipe qui la possèdent ou avec lesquelles elle a été partagée. |
Le corps de chaque erreur est { code, message, status, detail? }, et chaque réponse comporte un en-tête x-request-id qu’il est utile de journaliser. La liste complète des codes, les tableaux de résultats de scan et les recommandations relatives aux nouvelles tentatives figurent dans Erreurs et résultats de scan.
Étapes suivantes
Section intitulée « Étapes suivantes »- Conventions des requêtes — en-têtes de contexte, pagination et localisation.
- Erreurs et résultats de scan — le contrat d’échec complet.
- Fiches — types de champs, mises à jour, archivage, listes et recherche.
- Identifiants — lier et vérifier des identifiants physiques par rapport aux fiches (les points de terminaison
/api/v1/tags/*). - Fichiers — joindre des fichiers d’éléments probants aux fiches.
- Équipes et partage — accès entre équipes.
- Client TypeScript — une solution typée à la place de l’utilisation directe de
fetch. - Référence complète de l’API — chaque point de terminaison, généré à partir de la spécification OpenAPI. Le serveur d’API héberge également lui-même une référence interactive à l’adresse
https://apid.dustid.io/api/docset la spécification brute à l’adresse/api/openapi.json.