Téléverser un document

Ce guide montre comment téléverser un fichier vers une fiche document en une seule requête multipart/form-data, choisir dans quel dossier il atterrit, l'attacher à une autre fiche, et relire ses métadonnées de façon fiable. Il vous faut seulement un jeton d'API et votre URL de base, comme indiqué dans le guide de démarrage ; pour l'enveloppe d'écriture et la forme des erreurs de validation utilisées à l'intérieur de la partie data, consultez Créer, modifier et supprimer des fiches, et pour le fonctionnement général de related_to, consultez Lier des fiches entre elles.

1. La requête est multipart, pas JSON

Toute autre écriture de cette API envoie {"data": {"attributes": {...}}} en corps JSON avec Content-Type: application/json. Le téléversement d'un document est la seule exception : le corps est multipart/form-data avec deux partiesdata, la même enveloppe JSON sous forme de chaîne, et file, le contenu binaire brut :

curl -sS -X POST "https://app.initiative-crm.com/api/v1/documents" \
  -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  -F 'data={"data":{"attributes":{"notes_title":"Q3 report"}}}' \
  -F '[email protected]'

Ne définissez pas Content-Type vous-même — curl -F (et l'aide multipart de tout client HTTP) génère lui-même cet en-tête, y compris la limite (« boundary ») dont le serveur a besoin pour séparer les deux parties. Définir Content-Type: application/json manuellement sur cette requête, même avec les deux mêmes parties -F, fait que le serveur la refuse purement et simplement avant même de regarder le corps :

{"error":{"code":"UNSUPPORTED_MEDIA_TYPE","message":"POST /api/v1/documents requires multipart/form-data.","request_id":"..."}}

415. Le contrôle se fait uniquement sur l'en-tête, donc un Content-Type défini à la main casse la requête même si le corps multipart sous-jacent est par ailleurs correct.

2. Ce qui revient

filename, filetype et filesize ne sont jamais envoyés par le client — ils sont dérivés côté serveur à partir du fichier téléversé et écrits dans la fiche avant son enregistrement :

{
  "data": {
    "id": "1618",
    "type": "documents",
    "attributes": {
      "notes_title": "Q3 report",
      "folderid": 1,
      "notecontent": "",
      "filelocationtype": "I",
      "filestatus": true,
      "filename": "report.txt",
      "filesize": 26,
      "filetype": "text/plain",
      "fileversion": "",
      "filedownloadcount": 0,
      "createdtime": "2026-08-04T11:29:25Z",
      "modifiedtime": "2026-08-04T11:29:25Z",
      "note_no": "DOC-00001",
      "assigned_user_id": { "id": "7206", "label": "Taylor Morgan" },
      "creator": { "id": "7206", "label": "Taylor Morgan" },
      "modifiedby": { "id": "7206", "label": "Taylor Morgan" }
    }
  }
}
  • filename est le nom de fichier d'origine tiré de la partie file, pas un nom généré.
  • filetype est le type MIME détecté côté client (text/plain ici).
  • filesize est le nombre exact d'octets du contenu téléversé.
  • filelocationtype: "I" signifie que le fichier est stocké en interne (par opposition à un lien externe — non couvert par ce guide).
  • folderid prend par défaut la valeur 1 (le dossier Default) lorsqu'il est omis — voir l'étape 3.

Relisez le document : les trois sont bien intacts :

curl -sS -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  "https://app.initiative-crm.com/api/v1/documents/1618" \
  | jq '.data.attributes | {notes_title, filename, filetype, filesize, filelocationtype}'
{
  "notes_title": "Q3 report",
  "filename": "report.txt",
  "filetype": "text/plain",
  "filesize": 26,
  "filelocationtype": "I"
}

Identique octet pour octet à la réponse de création. Cet endpoint direct est l'endroit fiable pour vérifier les métadonnées d'un document — l'étape 5 comporte une mise en garde à propos d'un autre endpoint qui, lui, ne l'est pas.

3. Choisir un dossier

folderid se présente comme un champ picklist, avec la même forme de schéma que n'importe quel autre. Comme pour chaque module de cette API, le chemin du schéma prend le slug en minuscules, pas le nom interne avec majuscule (GET /api/v1/modules/Documents renvoie un 404 ; GET /api/v1/modules/documents est le bon — voir Champs, formats et mise en forme des réponses) :

curl -sS -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  "https://app.initiative-crm.com/api/v1/modules/documents" \
  | jq '.data.attributes.fields[] | select(.type == "picklist") | {name: .name, options: .options}'
{
  "name": "folderid",
  "options": [
    { "value": "1", "label": "Default" },
    { "value": "2", "label": "Documents" }
  ]
}

Définissez folderid sur l'une de ces valeurs à la création :

curl -sS -X POST "https://app.initiative-crm.com/api/v1/documents" -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  -F 'data={"data":{"attributes":{"notes_title":"Filed report","folderid":"2"}}}' \
  -F '[email protected]' | jq '.data.attributes | {notes_title, folderid}'
{ "notes_title": "Filed report", "folderid": 2 }

Une valeur hors de cette liste n'est pas rejetée. Contrairement à tout autre champ picklist de cette API (voir Créer, modifier et supprimer des fiches), un folderid inconnu ne provoque pas de 422 — il est accepté tel quel :

curl -sS -X POST "https://app.initiative-crm.com/api/v1/documents" -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  -F 'data={"data":{"attributes":{"notes_title":"Bad folder","folderid":"999999"}}}' \
  -F '[email protected]' -w '\n%{http_code}\n'
{"data":{"id":"1622","type":"documents","attributes":{"notes_title":"Bad folder","folderid":999999, "...": "..."}}}
201

201 — le document est quand même créé. Utilisez une valeur issue de l'appel de découverte de l'étape 3 — un identifiant de dossier non reconnu est accepté au lieu d'être rejeté, et vous vous retrouvez avec un document qui pointe vers un dossier qui n'existe pas.

4. L'attacher à une fiche

Passez related_to à l'intérieur de la même partie data, avec à la fois id et type :

curl -sS -X POST "https://app.initiative-crm.com/api/v1/documents" -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  -F 'data={"data":{"attributes":{"notes_title":"Attached report","related_to":{"id":"62","type":"contacts"}}}}' \
  -F '[email protected]'
{
  "data": { "id": "1626", "type": "documents", "attributes": { "notes_title": "Attached report", "...": "..." } },
  "meta": {
    "request_id": "...",
    "related_to": { "id": "62", "type": "contacts" }
  }
}

related_to est renvoyé en écho sous meta, pas à l'intérieur de data.attributes — ce n'est pas un champ stocké sur le document lui-même, donc il n'apparaît pas non plus sur un GET /documents/{id} ultérieur. Pour voir l'attachement depuis l'autre côté, listez-le :

curl -sS -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  "https://app.initiative-crm.com/api/v1/contacts/62/related/documents?per_page=5" | jq '.data'
[{ "id": "1626", "type": "documents", "attributes": { "notes_title": "Attached report", "...": "..." } }]

total: 1 confirme le lien. Ne lisez pas les métadonnées depuis cet endpoint, en revanche — filename, filesize, filetype, filedownloadcount, creator, modifiedby et createdtime n'y sont pas fiables. Utilisez-le pour confirmer que le lien existe, et récupérez le document lui-même (étape 2) pour ses métadonnées.

related_to n'accepte que des modules qui peuvent réellement posséder un document — le faire pointer vers, par exemple, un livre de prix échoue avec INVALID_OPTION sur related_to.type.

5. Relire un document

Déjà montré à l'étape 2 — répété ici comme le contrôle canonique « mon téléversement a-t-il survécu » :

curl -sS -H "Authorization: Bearer itv_YOUR_TOKEN_HERE" \
  "https://app.initiative-crm.com/api/v1/documents/1618" \
  | jq '.data.attributes | {notes_title, filename, filetype, filesize, filelocationtype}'
{
  "notes_title": "Q3 report",
  "filename": "report.txt",
  "filetype": "text/plain",
  "filesize": 26,
  "filelocationtype": "I"
}

Erreurs courantes

  • Envoyer les attributs du document comme des champs de formulaire individuels au lieu d'une seule partie JSON data. 422 VALIDATION_ERROR, {"field":"data","code":"REQUIRED","message":"A \"data\" JSON part is required."} — l'ensemble de l'objet attributes doit tenir dans une seule partie -F 'data=...' contenant du JSON, pas dans un -F 'notes_title=...' envoyé à côté.
  • Une partie data mal formée. 422 VALIDATION_ERROR, {"field":"data","code":"INVALID_JSON","message":"The \"data\" part must be valid JSON."} — la partie est d'abord lue comme une chaîne brute, puis seulement analysée.
  • related_to sans type. 422 VALIDATION_ERROR, {"field":"related_to","code":"INVALID_SHAPE","message":"related_to must include both \"id\" and \"type\"."}. (C'est le code propre à Documents pour cette erreur — Lier des fiches entre elles montre la même erreur sur une note/commentaire produisant plutôt INVALID_FORMAT ; le code que vous obtenez dépend du module, pas de l'erreur elle-même.)
  • Définir Content-Type: application/json à la main sur une requête multipart. 415 UNSUPPORTED_MEDIA_TYPE — laissez votre client HTTP générer lui-même le Content-Type multipart (avec sa limite) ; ne le remplacez jamais.
  • Omettre la partie file. 422 VALIDATION_ERROR, {"field":"file","code":"REQUIRED","message":"A \"file\" part must be uploaded."}data seul, sans fichier, est toujours rejeté ; il n'y a aucun moyen de créer un document sans pièce jointe via cet endpoint.

Did this page help you?