API GraphQL
L'API GraphQL de demarches-simplifiees.fr permet de consulter :
- les informations d'une démarche,
- la liste et les détails des dossiers d'une démarche,
- les détails d'un dossier spécifique.
Elle permet également de modifier certaines informations d'un dossier :
- Envoyer un message à l'usager d'un dossier ;
- Changer l'état d'un dossier (accepté, refusé, etc.)
3 minutes pour tout savoir de notre API
Cette API suit le paradigme GraphQL. Pour plus d'informations sur le fonctionnement de GraphQL en général, vous pouvez consulter les liens suivants :
L’API est accessible à l’adresse https://www.demarches-simplifiees.fr/api/v2/graphql (cette adresse n’est pas visitable dans un navigateur). Elle renvoie des données au format JSON, à travers un transport HTTPS.
Pour construire une requête et interpréter les réponses, consultez la documentation complète du schéma de l’API.
Une fois authentifié en tant qu’administrateur disposant d’un token, vous pouvez également accéder à l’éditeur de requêtes en ligne (attention, ne confondez pas cette adresse avec celle de l’endpoint, qui ressemble) :
Vous devez effectuer une démarche auprès des équipes DS et pour cela remplir une demande à l’adresse suivante.
Votre demande sera examinée, et vous recevrez un token de test, ainsi qu'un accès à un formulaire de test qui vous permettra d’effectuer des saisies de dossiers puis de tester les API.
Tous les appels sont authentifiés et doivent donc fournir un jeton valide qui est accessible dans la partie profil de l’administrateur. Ce jeton doit être fourni dans l’en-tête HTTP
Authorization
de la requête.Authorization: Bearer token=valeur_du_jeton
.post
https://www.demarches-simplifiees.fr
/api/v2/graphql
GraphQL
Pour tester l’API, le plus simple est d’effectuer une requête curl telle que ci dessous. Le principe est le même avec un autre client HTTP : remplacez votre_token et votre_numero_de_demarche par les valeurs souhaitez, et n’oubliez pas de préciser le content-type :
curl 'https://www.demarches-simplifiees.fr/api/v2/graphql' \
--header 'Authorization: Bearer <votre_token>' \
--header 'Content-Type: application/json' \
--data '{ "query": "query getDemarche($demarcheNumber: Int!) { demarche(number: $demarcheNumber) { id dossiers { nodes { id demandeur { ... on PersonnePhysique { civilite nom prenom } ... on PersonneMorale { siret } } } } } }", "variables": { "demarcheNumber": <votre_numero_de_demarche> } }'
Vous devriez alors obtenir des informations en sortie. S’il y a des dossiers dans votre démarche, cette requête vous donne les noms des demandeurs. Si la démarche s’adresse à des entreprises, vous aurez le numéro SIRET des demandeurs.
Voici une requête plus lisible, mais plus complexe, qui va chercher beaucoup d’informations sur les dossiers
query getDemarche(
$demarcheNumber: Int!
$state: DossierState
$order: Order
$first: Int
$after: String
$archived: Boolean
$revision: ID
$createdSince: ISO8601DateTime
$updatedSince: ISO8601DateTime
$deletedOrder: Order
$deletedFirst: Int
$deletedAfter: String
$deletedSince: ISO8601DateTime
$pendingDeletedOrder: Order
$pendingDeletedFirst: Int
$pendingDeletedAfter: String
$pendingDeletedSince: ISO8601DateTime
$includeGroupeInstructeurs: Boolean = false
$includeDossiers: Boolean = false
$includeDeletedDossiers: Boolean = false
$includePendingDeletedDossiers: Boolean = false
$includeRevision: Boolean = false
$includeService: Boolean = false
$includeChamps: Boolean = true
$includeAnotations: Boolean = true
$includeTraitements: Boolean = true
$includeInstructeurs: Boolean = true
$includeAvis: Boolean = false
$includeMessages: Boolean = false
$includeGeometry: Boolean = false
) {
demarche(number: $demarcheNumber) {
id
number
title
state
declarative
dateCreation
dateFermeture
activeRevision @include(if: $includeRevision) {
...RevisionFragment
}
groupeInstructeurs @include(if: $includeGroupeInstructeurs) {
...GroupeInstructeurFragment
}
service @include(if: $includeService) {
...ServiceFragment
}
dossiers(
state: $state
order: $order
first: $first
after: $after
archived: $archived
createdSince: $createdSince
updatedSince: $updatedSince
revision: $revision
) @include(if: $includeDossiers) {
pageInfo {
...PageInfoFragment
}
nodes {
...DossierFragment
}
}
pendingDeletedDossiers(
order: $pendingDeletedOrder
first: $pendingDeletedFirst
after: $pendingDeletedAfter
deletedSince: $pendingDeletedSince
) @include(if: $includePendingDeletedDossiers) {
pageInfo {
...PageInfoFragment
}
nodes {
...DeletedDossierFragment
}
}
deletedDossiers(
order: $deletedOrder
first: $deletedFirst
after: $deletedAfter
deletedSince: $deletedSince
) @include(if: $includeDeletedDossiers) {
pageInfo {
...PageInfoFragment
}
nodes {
...DeletedDossierFragment
}
}
}
}
query getGroupeInstructeur(
$groupeInstructeurNumber: Int!
$state: DossierState
$order: Order
$first: Int
$after: String
$archived: Boolean
$revision: ID
$createdSince: ISO8601DateTime
$updatedSince: ISO8601DateTime
$deletedOrder: Order
$deletedFirst: Int
$deletedAfter: String
$deletedSince: ISO8601DateTime
$includeDossiers: Boolean = false
$includeDeletedDossiers: Boolean = false
$includeChamps: Boolean = true
$includeAnotations: Boolean = true
$includeTraitements: Boolean = true
$includeInstructeurs: Boolean = true
$includeAvis: Boolean = false
$includeMessages: Boolean = false
$includeGeometry: Boolean = false
) {
groupeInstructeur(number: $groupeInstructeurNumber) {
id
number
label
instructeurs @include(if: $includeInstructeurs) {
id
email
}
dossiers(
state: $state
order: $order
first: $first
after: $after
archived: $archived
createdSince: $createdSince
updatedSince: $updatedSince
revision: $revision
) @include(if: $includeDossiers) {
pageInfo {
...PageInfoFragment
}
nodes {
...DossierFragment
}
}
deletedDossiers(
order: $deletedOrder
first: $deletedFirst
after: $deletedAfter
deletedSince: $deletedSince
) @include(if: $includeDeletedDossiers) {
pageInfo {
...PageInfoFragment
}
nodes {
...DeletedDossierFragment
}
}
}
}
query getDossier(
$dossierNumber: Int!
$includeRevision: Boolean = false
$includeService: Boolean = false
$includeChamps: Boolean = true
$includeAnotations: Boolean = true
$includeTraitements: Boolean = true
$includeInstructeurs: Boolean = true
$includeAvis: Boolean = false
$includeMessages: Boolean = false
$includeGeometry: Boolean = false
) {
dossier(number: $dossierNumber) {
...DossierFragment
demarche {
...DemarcheDescriptorFragment
}
}
}
query getDemarcheDescriptor(
$demarche: FindDemarcheInput!
$includeRevision: Boolean = false
$includeService: Boolean = false
) {
demarcheDescriptor(demarche: $demarche) {
...DemarcheDescriptorFragment
}
}
fragment ServiceFragment on Service {
nom
siret
organisme
typeOrganisme
}
fragment GroupeInstructeurFragment on GroupeInstructeur {
id
number
label
instructeurs @include(if: $includeInstructeurs) {
id
email
}
}
fragment DossierFragment on Dossier {
id
number
archived
state
dateDerniereModification
dateDepot
datePassageEnConstruction
datePassageEnInstruction
dateTraitement
dateExpiration
dateSuppressionParUsager
motivation
motivationAttachment {
...FileFragment
}
attestation {
...FileFragment
}
pdf {
url
}
usager {
email
}
groupeInstructeur {
...GroupeInstructeurFragment
}
demandeur {
__typename
... on PersonnePhysique {
civilite
nom
prenom
}
... on PersonneMoraleIncomplete { siret }
...PersonneMoraleFragment
}
demarche {
revision {
id
}
}
instructeurs @include(if: $includeInstructeurs) {
id
email
}
traitements @include(if: $includeTraitements) {
state
emailAgentTraitant
dateTraitement
motivation
}
champs @include(if: $includeChamps) {
...ChampFragment
...RootChampFragment
}
annotations @include(if: $includeAnotations) {
...ChampFragment
...RootChampFragment
}
avis @include(if: $includeAvis) {
...AvisFragment
}
messages @include(if: $includeMessages) {
...MessageFragment
}
}
fragment DemarcheDescriptorFragment on DemarcheDescriptor {
id
number
title
description
state
declarative
dateCreation
datePublication
dateDerniereModification
dateDepublication
dateFermeture
notice {
url
}
deliberation {
url
}
demarcheUrl
cadreJuridiqueUrl
service @include(if: $includeService) {
...ServiceFragment
}
revision @include(if: $includeRevision) {
...RevisionFragment
}
}
fragment DeletedDossierFragment on DeletedDossier {
id
number
dateSupression
state
reason
}
fragment RevisionFragment on Revision {
id
datePublication
champDescriptors {
...ChampDescriptorFragment
... on RepetitionChampDescriptor {
champDescriptors {
...ChampDescriptorFragment
}
}
}
annotationDescriptors {
...ChampDescriptorFragment
... on RepetitionChampDescriptor {
champDescriptors {
...ChampDescriptorFragment
}
}
}
}
fragment ChampDescriptorFragment on ChampDescriptor {
__typename
id
label
description
required
... on DropDownListChampDescriptor {
options
otherOption
}
... on MultipleDropDownListChampDescriptor {
options
}
... on LinkedDropDownListChampDescriptor {
options
}
... on PieceJustificativeChampDescriptor {
fileTemplate {
...FileFragment
}
}
... on ExplicationChampDescriptor {
collapsibleExplanationEnabled
collapsibleExplanationText
}
}
fragment AvisFragment on Avis {
id
question
reponse
dateQuestion
dateReponse
claimant {
email
}
expert {
email
}
attachments {
...FileFragment
}
}
fragment MessageFragment on Message {
id
email
body
createdAt
attachments {
...FileFragment
}
}
fragment GeoAreaFragment on GeoArea {
id
source
description
geometry @include(if: $includeGeometry) {
type
coordinates
}
... on ParcelleCadastrale {
commune
numero
section
prefixe
surface
}
}
fragment RootChampFragment on Champ {
... on RepetitionChamp {
rows {
champs {
...ChampFragment
}
}
}
... on CarteChamp {
geoAreas {
...GeoAreaFragment
}
}
... on DossierLinkChamp {
dossier {
id
number
state
}
}
}
fragment ChampFragment on Champ {
id
__typename
label
stringValue
... on DateChamp {
date
}
... on DatetimeChamp {
datetime
}
... on CheckboxChamp {
checked: value
}
... on DecimalNumberChamp {
decimalNumber: value
}
... on IntegerNumberChamp {
integerNumber: value
}
... on CiviliteChamp {
civilite: value
}
... on LinkedDropDownListChamp {
primaryValue
secondaryValue
}
... on MultipleDropDownListChamp {
values
}
... on PieceJustificativeChamp {
files {
...FileFragment
}
}
... on AddressChamp {
address {
...AddressFragment
}
}
... on CommuneChamp {
commune {
name
code
}
departement {
name
code
}
}
... on DepartementChamp {
departement {
name
code
}
}
... on RegionChamp {
region {
name
code
}
}
... on PaysChamp {
pays {
name
code
}
}
... on SiretChamp {
etablissement {
...PersonneMoraleFragment
}
}
}
fragment PersonneMoraleFragment on PersonneMorale {
siret
siegeSocial
naf
libelleNaf
address {
...AddressFragment
}
entreprise {
siren
capitalSocial
numeroTvaIntracommunautaire
formeJuridique
formeJuridiqueCode
nomCommercial
raisonSociale
siretSiegeSocial
codeEffectifEntreprise
dateCreation
nom
prenom
attestationFiscaleAttachment {
...FileFragment
}
attestationSocialeAttachment {
...FileFragment
}
}
association {
rna
titre
objet
dateCreation
dateDeclaration
datePublication
}
}
fragment FileFragment on File {
filename
contentType
checksum
byteSize: byteSizeBigInt
url
}
fragment AddressFragment on Address {
label
type
streetAddress
streetNumber
streetName
postalCode
cityName
cityCode
departmentName
departmentCode
regionName
regionCode
}
fragment PageInfoFragment on PageInfo {
hasPreviousPage
hasNextPage
endCursor
}
# Tout les dossiers d’une démarche
{
"query": <query>,
"operationName": "getDemarche",
"variables": {
"demarcheNumber": 1234,
"includeDossiers" true,
"includeChamps": true
}
}
# Tout les dossiers "en_instruction" d’une démarche
{
"query": <query>,
"operationName": "getDemarche",
"variables": {
"demarcheNumber": 1234,
"state": "en_instruction",
"includeDossiers": true,
"includeChamps": true
}
}
# Tout les dossiers en attente de suppression d’une démarche
{
"query": <query>,
"operationName": "getDemarche",
"variables": {
"demarcheNumber": 1234,
"includePendingDeletedDossiers": true
}
}
# Tout les dossiers supprimés d’une démarche
{
"query": <query>,
"operationName": "getDemarche",
"variables": {
"demarcheNumber": 1234,
"includeDeletedDossiers": true
}
}
# Tout les dossiers d’un groupe instructeur
{
"query": <query>,
"operationName": "getGroupeInstructeur",
"variables": {
"groupeInstructeurNumber": 1234,
"includeDossiers": true,
"includeChamps": true
}
}
# Un dossier par son numero
{
"query": <query>,
"operationName": "getDossier",
"variables": {
"dossierNumber": 1234,
"includeChamps": true,
"includeAnotations": true,
"includeAvis": true
}
}
La pagination sur l’API GraphQL se fait par « curseur ». Ça veut dire que pour récupérer la prochaine page il faut passer à l’API le « curseur » de la fin de la page précédente.
Voici un exemple. On commence par faire une query pour récupérer les 100 premiers dossiers :
query getDemarche($demarcheNumber: Int!, $after: String) {
demarche(number: $demarcheNumber) {
dossiers(first: 100, after: $after) {
pageInfo {
endCursor
hasNextPage
}
nodes {
id
number
}
}
}
}
{
"query": <query>,
"operationName": "getDemarche",
"variables": {
"demarcheNumber": 1234
}
}
Dans le résultat obtenu il faut lire la valeur du « curseur » dans le champ
demarche.dossiers.pageInfo.endCursor
. On peut passer alors ce « curseur » comme argument after
dans la prochaine query. Et ainsi de suite jusqu’à ce que le champ demarche.dossiers.pageInfo.hasNextPage
soit égal à false
.query getDemarche($demarcheNumber: Int!, $after: String) {
demarche(number: $demarcheNumber) {
dossiers(first: 100, after: $after) {
pageInfo {
endCursor
hasNextPage
}
nodes {
id
number
}
}
}
}
{
"query": <query>,
"operationName": "getDemarche",
"variables": {
"demarcheNumber": 1234,
"after" "abc"
}
}
Certaines opérations sur dossier sont accessibles via l’API :
- Affecter un dossier à un groupe instructeur
- Changer l’état du dossier
- Envoyer un message à l’usager qui a déposé le dossier
- Ajouter des annotations privées sur un dossier
Attention ! Les identifiants utilisés dans les variables
input
des mutations sont les identifiants « techniques » que vous devez récupérer dans les réponses des requêtes précédentes. En particulier, l'identifiant du dossier est différent du « numéro de dossier » :query getDossierIdByNumber($dossierNumber: Int!) {
dossier(number: $dossierNumber) {
id
}
}
{
"query": <query>,
"operationName": "getDossierIdByNumber",
"variables": {
"dossierNumber": 1234
}
}
Si un identifiant d’instructeur est demandé, il s’agit d’identifier l’instructeur qui sera enregistré dans le système comme responsable de l’opération. Pour récupérer la liste des instructeurs avec leurs identifiants, vous avez plusieurs possibilités.
Vous pouvez récupérer tous les instructeurs qui suivent un dossier :
query getInstructeursForDossier($dossierNumber: Int!) {
dossier(number: $dossierNumber) {
instructeurs {
id
email
}
}
}
{
"query": <query>,
"operationName": "getInstructeursForDossier",
"variables": {
"dossierNumber": 1234
}
}
Vous pouvez récupérer tous les instructeurs d'un « groupe instructeur » :
query getInstructeursForGroupeInstructeur($groupeInstructeurNumber: Int!) {
groupeInstructeur(number: $groupeInstructeurNumber) {
instructeurs {
id
email
}
}
}
{
"query": <query>,
"operationName": "getInstructeursForGroupeInstructeur",
"variables": {
"groupeInstructeurNumber": 1234
}
}
mutation dossierArchiver($input: DossierArchiverInput!) {
dossierArchiver(input: $input) {
dossier {
id
}
errors {
message
}
}
}
mutation dossierPasserEnInstruction($input: DossierPasserEnInstructionInput!) {
dossierPasserEnInstruction(input: $input) {
dossier {
id
}
errors {
message
}
}
}
mutation dossierAccepter($input: DossierAccepterInput!) {
dossierAccepter(input: $input) {
dossier {
id
}
errors {
message
}
}
}
mutation dossierRefuser($input: DossierRefuserInput!) {
dossierRefuser(input: $input) {
dossier {
id
}
errors {
message
}
}
}
mutation dossierClasserSansSuite($input: DossierClasserSansSuiteInput!) {
dossierClasserSansSuite(input: $input) {
dossier {
id
}
errors {
message
}
}
}
# Archiver un dossier
{
"query": <query>,
"operationName": "dossierArchiver",
"variables": {
"input": {
"dossierId": "UHJvY4VkdXKlLTI5NTgw",
"instructeurId": "OPJvtN7kdXKlLTI4NTlf"
}
}
}
# Passer un dossier en instruction
{
"query": <query>,
"operationName": "dossierPasserEnInstruction",
"variables": {
"input": {
"dossierId": "UHJvY4VkdXKlLTI5NTgw",
"instructeurId": "OPJvtN7kdXKlLTI4NTlf"
}
}
}
# Accepter un dossier
{
"query": <query>,
"operationName": "dossierAccepter",
"variables": {
"input": {
"dossierId": "UHJvY4VkdXKlLTI5NTgw",
"instructeurId": "OPJvtN7kdXKlLTI4NTlf",
"motivation": "J’accepte ce dossier"
}
}
}
# Refuser un dossier
{
"query": <query>,
"operationName": "dossierRefuser",
"variables": {
"input": {
"dossierId": "UHJvY4VkdXKlLTI5NTgw",
"instructeurId": "OPJvtN7kdXKlLTI4NTlf",
"motivation": "Je refuse ce dossier"
}
}
}
# Classer un dossier sans suite
{
"query": <query>,
"operationName": "dossierClasserSansSuite",
"variables": {
"input": {
"dossierId": "UHJvY4VkdXKlLTI5NTgw",
"instructeurId": "OPJvtN7kdXKlLTI4NTlf",
"motivation": "Je classe ce dossier sans suite"
}
}
}
Pour plus d’informations sur le format des requêtes et des réponses, consultez la documentation complète du schéma de l’API.
Vous pouvez envoyer des messages au dépositaire du dossier. Une notification par email sera envoyée et le message apparaitra dans l’historique des messages.
mutation dossierEnvoyerMessage($input: DossierEnvoyerMessageInput!) {
dossierEnvoyerMessage(input: $input) {
message {
id
}
errors {
message
}
}
}
<