Points de terminaison

Requêtes et réponses des points de terminaison de l’API d’administration de l’espace de travail

Il s’agit de la référence des requêtes/réponses pour chaque point de terminaison /v2/admin. L’authentification, l’idempotence, les limites de fréquence, la création des jetons et le sens des portées sont documentés dans API d’administration de l’espace de travail. Les types de champs réutilisables et les exemples figurent dans Schémas et exemples. Lisez chaque ligne de point de terminaison avec Contrat des réponses et des erreurs, qui répertorie tous les corps possibles de réponse d’échec communs ou spécifiques.

Contrat commun

Chaque chemin commence par l’origine du service app-api déployé. L’espace de travail est déduit exclusivement du jeton authentifié ; les clients ne doivent pas envoyer cpoId. Toutes les valeurs de chemin de ressource :...Id documentées sont des entiers décimaux positifs. Chaque requête nécessite exactement un :

Authorization: Bearer <workspace-access-token>

Les requêtes présentées avec un corps JSON nécessitent Content-Type: application/json. Toutes les mutations nécessitent Idempotency-Key (POST, PUT, PATCH et DELETE), y compris POST /v2/admin/reports/send ; sauf mention contraire sur la ligne, le type de contenu en cas de réussite est application/json, les dates JSON sont des chaînes RFC 3339 et les nombres sont des nombres JSON.

Chaque requête peut inclure X-Request-Id et X-Error-Format: envelope ; voir Contrat des réponses et des erreurs. Ces en-têtes ne modifient pas les schémas de réussite.

Notation utilisée ci-dessous :

  • none signifie aucun corps de requête. Un DELETE documentant un corps JSON exige toujours ce corps.
  • ? indique une propriété ou un paramètre de requête facultatif.
  • T | null signifie que la propriété est présente et peut être null.
  • 204 empty signifie aucun corps de réponse.
  • CursorPage<T> signifie { items: T[], nextCursor: number|string|null }, sauf lorsque la ligne nomme explicitement un objet page ou pagination.
  • Tous les cas de ressources imbriquées introuvables, y compris les ID d’autres espaces de travail, utilisent 404 { "error": "Workspace resource not found", "code": "RESOURCE_NOT_FOUND" }, sauf si une fonctionnalité documente un conflit plus précis.

Schémas de requête communs

Entrées de l’espace de travail et du site

WorkspacePatch = {
  name?: string(1..100), email?: email|null, website?: string|null,
  addressLine1?: string|null, addressLine2?: string|null,
  postalCode?: string|null, city?: string|null, state?: string|null,
  countryId?: positive integer
} // at least one property

SiteInput = {
  name: string(1..255), description?: string|null = null,
  addressLine1?: string|null = null, addressLine2?: string|null = null,
  postalCode?: string|null = null, city?: string|null = null, state?: string|null = null,
  countryId: positive integer, timezoneId: positive integer,
  latitude?: number[-90,90]|null = null, longitude?: number[-180,180]|null = null
}

Entrée de gestion de la puissance

PowerConfigurationInput = { pools: PowerPoolInput[0..200] }
PowerPoolInput = {
  id?: positive integer|null, name?: string(0..255),
  limit: integer[0,100000000], meterSources?: ExternalMeterInput[0..16],
  children?: (PowerPoolInput | ChargerPoolChild)[0..1000]
}
ChargerPoolChild = { chargepointId: positive integer, priority?: integer[-1000000,1000000] }
ExternalMeterInput = {
  id?: positive integer|null, sourceId?: positive integer|null,
  provider?: "perific", name?: string(0..255), externalId: string(1..80),
  measurementMode?: "available_headroom"|"external_non_ev_load_only"|"total_site_load_including_ev",
  valueMode?: "perific_relative"|"perific_direct"|"perific_meter",
  reserveA?: integer[0,100000], offlineSafeA?: integer[0,100000],
  ttlSeconds?: integer[30,86400], enabled?: boolean
}

Entrées des bornes

ChargerCreate = { name: string(1..40), identifier: string(1..40), password?: string(0..40) = "", siteId: positive integer }
ChargerPatch = {
  name?: string(1..40), siteId?: positive integer, invertedRfid?: boolean,
  smartMode?: integer[0,3], automationDisabled?: boolean
} // at least one
ConnectorPatch = {
  connectorFormatId: positive integer, connectorTypeId: positive integer,
  powerTypeId: positive integer, maxVoltage: integer[1,10000],
  maxAmperage: integer[1,10000], maxElectricPower: integer[1,100000000]
}

Entrées des rapports

ReportFilters = {
  from?: RFC3339, to?: RFC3339, siteId?: positive integer,
  chargerId?: positive integer, userId?: positive integer,
  anonymous?: true|false|1|0, active?: true|false|1|0
}
ReportScheduleInput = {
  enabled: boolean, recipientEmail: email(max 255; may be empty only when disabled), format: "csv"|"pdf",
  sendDay: integer[1,28], sendTime: UTC "HH:mm"
}

Les plages de rapport doivent satisfaire from < to et ne peuvent pas dépasser 90 jours. Si elles sont omises, les 90 derniers jours sont utilisés. userId et anonymous=true s’excluent mutuellement. Les exports planifiés rattrapent une minute UTC manquée et utilisent des tentatives limitées : attente de 5/10 minutes, bail de 30 minutes pour une réservation devenue obsolète, trois tentatives.

Entrées des tarifs

TariffInput = {
  name: string(1..64),
  exactly one of currencyId: positive integer, currencyCode: 3 letters, currency: 3 letters,
  biddingZoneId?: positive integer|null, tax?: finite number
}
TariffComponentInput = {
  typeId: positive integer, seasonId: positive integer, priority?: finite number,
  price: finite number, validFromMonth?: finite number, validToMonth?: finite number,
  validFromWeekDay?: finite number, validToWeekDay?: finite number,
  validFromHour?: finite number, validToHour?: finite number
}
TariffAssignmentCreateInput = { tariffId: positive integer, connectorId?: nonnegative integer }
TariffAssignmentUpdateInput = { tariffId: positive integer, connectorId: positive integer }

Entrées des profils de recharge

ChargingProfileInput = {
  label: string(1..40), locationId: positive integer, stackLevel: nonnegative number,
  purposeId: positive integer, kindId: positive integer,
  recurrencyKindId?: positive integer|null, validFrom?: RFC3339|null,
  validTo?: RFC3339|null, startSchedule?: RFC3339|null,
  rateUnitId: positive integer, minChargingRate?: nonnegative number|null
}
ChargingProfileCreate = ChargingProfileInput & { schedule?: SchedulePeriod[0..256] }
SchedulePeriod = { startPeriod: nonnegative number, limit: nonnegative number, numberOfPhases?: positive integer|null }
ChargingScheduleInput = { rateUnitId: positive integer, periods: SchedulePeriod[1..256] }
ProfileAssignmentInput = { profileId: positive integer, connectorId?: nonnegative integer }

Entrées des utilisateurs

UserCreate = { email: email(max 255), name: string(1..255) }
InvitationInput = {
  email: email, name?: string|null, rfid?: string|null,
  allChargers?: boolean, chargerIds?: unique positive integer[0..500]
} // chargerIds must be non-empty unless allChargers=true
AccessInput = { allChargers?: boolean, chargerIds?: unique positive integer[0..500] }
LimitCreate = { limitTypeId: positive integer, energyValue?: nonnegative integer = 0, timeValue?: nonnegative integer = 0 }
LimitPatch = { limitTypeId?: positive integer, energyValue?: nonnegative integer, timeValue?: nonnegative integer }

Lors de la création d’une limite, au moins une valeur parmi energyValue et timeValue doit être supérieure à zéro. Les espaces en début et en fin d’e-mail sont supprimés et les lettres converties en minuscules. Les UID RFID sont acceptés uniquement en écriture et masqués dans les réponses de lecture.

Résumé des DTO de réponse

Les tableaux des points de terminaison montrent chaque enveloppe de réponse de premier niveau. Voici les principaux DTO réutilisables contenus dans ces enveloppes :

DTOChamps
Workspaceid, name, email, website, address { line1, line2, postalCode, city, state, countryId, countryCode, countryName }, createdAt, updatedAt
Siteid, name, description, address, timezone { id, name }, latitude, longitude, chargerCount ; le détail ajoute chargers normalisé et l’état du connecteur
GeocodeCandidateplaceId, displayName, latitude, longitude ; les identifiants du fournisseur et ses métadonnées spécifiques ne sont jamais renvoyés
MetricsWindowfrom, to, limites/nombres de sessions et totalsCapped le cas échéant
Chargerid, name, identifier, cpoId, site, status normalisé, isPublic, indicateurs RFID/intelligent/automatisation, provisioned, connection, evses, connectors, createdAt, updatedAt ; le détail ajoute configuration expurgé, firmware et les décomptes
ConnectorportId, connectorId, ID de référentiel de connecteur, maxVoltage, maxAmperage, maxElectricPower
ReportSessionid, borne/site, ID de port et de connecteur, données utilisateur minimales, active, energyWh, début/fin/durée et coûts nets/taxes/totaux par devise
ReportScheduleid, workspaceId, enabled, recipientEmail, format, sendDay/sendTime UTC, métadonnées de création/mise à jour et métadonnées sûres de dernière livraison, y compris le nombre de tentatives
Tariffid, cpoId, name, objets de type/site/devise/zone de marché, tax, fallbackPrice, price effectif, components, assignmentCount
ChargingProfileid, cpoId, label, description, stackLevel, objets d’emplacement/usage/type/récurrence/unité de limitation, validité, programmation, assignmentCount, horodatages
Userid, membershipId, name, email, rfids masqué, access de borne, limitCount ; le détail ajoute phone, createdAt et limits. Les identifiants d’autorisation OCPP tels que contractId ne sont jamais renvoyés.
RFIDid, maskedUid, name, type, valid ; l’UID brut n’est jamais renvoyé
AccessallChargers, chargerIds trié et items contenant les métadonnées d’accès/borne/site/expiration
Limitid, limitTypeId, label, energyValue, timeValue
LocalUserDTO de liste : id, customerId, name, email, expiresAt, systemManaged. Les mutations d’utilisateur local utilisent les structures distinctes LocalUserMutation/LocalUserUpdate de Schémas et exemples.

Les objets de référentiel sont renvoyés par les routes de liste et de détail pour permettre aux appelants de découvrir les ID valides. Traitez les nouveaux champs inconnus comme des extensions compatibles avec les versions futures et fondez vos décisions uniquement sur les champs stables documentés et les codes d’erreur.

Points de terminaison de l’espace de travail

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/adminworkspaces:readCorps : aucun200 { workspace: Workspace }
PATCH /v2/adminworkspaces:updateCorps : WorkspacePatch200 { workspace: Workspace }
GET /v2/admin/dashboardworkspaces:read + reports:readCorps/paramètres de requête : aucun200 { window, stats { chargerCount, onlineChargerCount, siteCount, userCount }, sessions: DashboardSession[], analytics } ; les sessions sont limitées à 1 000 sur 90 jours

Points de terminaison des sites et de gestion de la puissance

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/admin/sitessites:readCorps/paramètres de requête : aucun200 { items: Site[], metricsWindow: MetricsWindow, lookups { countries, timezones } }
GET /v2/admin/sites/geocodesites:readParamètres : street facultatif (160 max.), city, state, country (100 max. chacun), countryCode (2 max.) ; en-tête Accept-Language facultatif ; au moins deux caractères sont requis au total pour rue/ville/région200 { results: GeocodeCandidate[] } ; les recherches identiques en cours sont fusionnées et le fournisseur utilise une file séquentielle bornée ainsi qu’un cache serveur borné ; les recherches invalides renvoient 400 INVALID_GEOCODE_QUERY, la saturation de la file renvoie 429 GEOCODING_BUSY avec Retry-After et les défaillances du fournisseur renvoient 502 GEOCODING_UNAVAILABLE
POST /v2/admin/sitessites:createCorps : SiteInput201 { site: Site }
GET /v2/admin/sites/:siteIdsites:read + reports:readCorps/paramètres de requête : aucun200 { site: SiteDetail, recentSessions: SiteSession[], totals, metricsWindow }
PATCH /v2/admin/sites/:siteIdsites:updateCorps : SiteInput complet200 { site: Site }
DELETE /v2/admin/sites/:siteIdsites:deleteCorps : aucun204 empty ; un site non vide renvoie 409 SITE_NOT_EMPTY
GET /v2/admin/sites/:siteId/power-managementsites:power-managementCorps/paramètres de requête : aucun200 { configuration: PowerConfiguration } contenant le site du périmètre, les bornes/connecteurs, groupes, sources de mesure, attributions et états de compatibilité
PUT /v2/admin/sites/:siteId/power-managementsites:power-managementCorps : PowerConfigurationInput200 { configuration: PowerConfiguration } ; un refus OCPP, un délai dépassé ou une modification concurrente renvoie 409 POWER_MANAGEMENT_CONFLICT sans appliquer le remplacement

Points de terminaison des bornes

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/admin/chargerschargers:readCorps/paramètres de requête : aucun200 { items: Charger[], metricsWindow: MetricsWindow, connectorLookups { formats, types, powerTypes } }
POST /v2/admin/chargerschargers:createCorps : ChargerCreate ; l’identifiant est converti en majuscules201 { charger: Charger } ; le mot de passe n’est jamais renvoyé
GET /v2/admin/chargers/:chargerIdchargers:readCorps/paramètres de requête : aucun200 { charger: ChargerDetail }
PATCH /v2/admin/chargers/:chargerIdchargers:updateCorps : ChargerPatch200 { charger: ChargerDetail }
DELETE /v2/admin/chargers/:chargerIdchargers:deleteCorps : aucun204 empty
PATCH /v2/admin/chargers/:chargerId/credentialschargers:credentialsCorps : { password: string(0..40) }200 { success: true }
PATCH /v2/admin/chargers/:chargerId/connectors/:portIdchargers:connector-settingsCorps : ConnectorPatch200 { connector: Connector }
PATCH /v2/admin/chargers/:chargerId/public-listingchargers:publicCorps : { isPublic: boolean }200 { isPublic: boolean } ; des conditions de préparation non remplies renvoient 409 CHARGER_CONFLICT
GET /v2/admin/chargers/:chargerId/public-readinesschargers:readCorps/paramètres de requête : aucun200 { readiness: { ready, missingRequirements, connectorIds, missingTariffConnectorIds, ...commercialState } }
GET /v2/admin/chargers/:chargerId/logschargers:readParamètres : cursor?, limit?=50 (1..200), from?, to?, action?, direction?, levelId?200 { items: RedactedOcppLog[], nextCursor: number or null }
GET /v2/admin/chargers/:chargerId/sessionschargers:read + reports:readParamètres : page?=1, limit?=25 (1..100), from?, to?, customerId?: positive integer or "unknown"200 { items: ChargerSession[], pagination { page, limit, total, totalPages }, filters { customers }, totals, totalsCapped }
GET /v2/admin/chargers/:chargerId/qrchargers:readParamètres : connector?: integer[0,255]200 image/png binaire, 300 px, Content-Disposition: inline, Cache-Control: no-store
GET /v2/admin/chargers/:chargerId/composite-scheduleschargers:readParamètres : cursor?, limit?=20 (1..50), connectorId?200 { items: CompositeSchedule[], nextCursor: number or null }
GET /v2/admin/chargers/:chargerId/configurationchargers:readCorps/paramètres de requête : aucun200 { items: ChargerConfiguration[] } ; les valeurs assimilables à des secrets sont [REDACTED]
PATCH /v2/admin/chargers/:chargerId/configurationchargers:configurationCorps : { key: string(1..50), value: string(0..500) }200 { status: string }, où le statut est le résultat de la borne/OCPP
POST /v2/admin/chargers/:chargerId/configuration/reloadchargers:configurationCorps : aucun200 { status: string }
GET /v2/admin/chargers/:chargerId/firmwarechargers:readCorps/paramètres de requête : aucun200 { firmware: { status, notifiedAt, currentVersion, bootNotifiedAt } }
POST /v2/admin/chargers/:chargerId/firmwarechargers:firmwareCorps : { url: HTTP-or-HTTPS URL, max 2048 characters }202 { status: string }
POST /v2/admin/chargers/:chargerId/commands/resetchargers:commandsCorps : { resetType: "Soft" or "Hard" }200 { status: string }
POST /v2/admin/chargers/:chargerId/commands/availabilitychargers:commandsCorps : { availabilityType: "Operative" or "Inoperative" }200 { status: string }
POST /v2/admin/chargers/:chargerId/commands/triggerchargers:commandsCorps : { messageType: "BootNotification", "Heartbeat", "StatusNotification", or "MeterValues" }200 { status: string, connectorResults: { connectorId, status }[] }
POST /v2/admin/chargers/:chargerId/commands/unlockchargers:commandsCorps : { connectorId: positive integer }200 { status: string }
POST /v2/admin/chargers/:chargerId/commands/composite-schedulechargers:commandsCorps : { connectorId: nonnegative integer, durationSeconds?: integer[60,604800], chargingRateUnit?: "W" or "A" }200 { status: string, connectorId: number, compositeSchedule: CompositeSchedule or null }

Points de terminaison des rapports

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/admin/reports/datareports:readParamètres : ReportFilters, cursor? opaque, limit?=50 (1..100)200 { items: ReportSession[], pageTotals, page { limit, nextCursor }, range { from, to, toExclusive: true } }
GET /v2/admin/reports/schedulereports:readCorps/paramètres de requête : aucun200 { schedule: ReportSchedule or null }
PUT /v2/admin/reports/schedulereports:schedulesCorps : schéma strict ReportScheduleInput200 { schedule: ReportSchedule }
POST /v2/admin/reports/sendreports:sendCorps : aucun ; l’en-tête Idempotency-Key est obligatoire200 { sent: true, format: "csv" or "pdf", rowCount: number, range { from, to } } ; l’absence de programmation enregistrée renvoie 409 REPORT_SCHEDULE_REQUIRED
GET /v2/admin/reports/measurementsreports:readParamètres : transactionLogId obligatoire, cursor? opaque, limit?=500 (1..1000)200 { transactionLogId, measurements: { name, unit, lines[] }[], page { limit, nextCursor, sampleValuesTruncated, sampleLimitPerMeterValue: 64 } }
GET /v2/admin/reports/exportreports:exportParamètres : ReportFilters, format?="csv" or "pdf"200 binaire CSV/PDF avec nom de fichier de pièce jointe, Cache-Control: private, no-store, X-Content-Type-Options: nosniff ; plus de 2 000 lignes renvoient 422 REPORT_EXPORT_ROW_LIMIT

Points de terminaison des tarifs

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/admin/tariffstariffs:readCorps/paramètres de requête : aucun200 { items: Tariff[], lookups { currencies, biddingZones, currencyRates, componentTypes, seasons, tariffTypes } }
POST /v2/admin/tariffstariffs:createCorps : TariffInput201 { tariff: Tariff }
GET /v2/admin/tariffs/:tariffIdtariffs:readCorps/paramètres de requête : aucun200 { tariff: Tariff, lookups }
PATCH /v2/admin/tariffs/:tariffIdtariffs:updateCorps : TariffInput complet200 { tariff: Tariff }
PUT /v2/admin/tariffs/:tariffId/componentstariffs:componentsCorps : { components: TariffComponentInput[0..256] }200 { tariff: Tariff, components: TariffComponent[] }
DELETE /v2/admin/tariffs/:tariffIdtariffs:deleteCorps : aucun204 empty ; les références depuis des enregistrements de recharge renvoient 409 TARIFF_CONFLICT
GET /v2/admin/tariffs/:tariffId/market-pricestariffs:readParamètres : startsAt obligatoire, horodatage Unix non négatif200 { items: { id, value, currencyId, startsAt, endsAt, startsAtIso, endsAtIso, intervalMinutes }[], truncated: boolean } ; au maximum 10 000 lignes
GET /v2/admin/chargers/:chargerId/tariff-assignmentschargers:tariff-assignments + tariffs:readCorps/paramètres de requête : aucun200 { items: TariffAssignment[], tariffs: Tariff[] }
POST /v2/admin/chargers/:chargerId/tariff-assignmentschargers:tariff-assignments + tariffs:assignCorps : TariffAssignmentCreateInput ; omettre connectorId ou connectorId=0 signifie tous les connecteurs201 { assignments: TariffAssignment[] }
PATCH /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentIdchargers:tariff-assignments + tariffs:assignCorps : TariffAssignmentUpdateInput ; le connecteur est obligatoire et ne peut pas être 0200 { assignment: TariffAssignment }
DELETE /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentIdchargers:tariff-assignments + tariffs:assignCorps : aucun204 empty

TariffAssignment contient id, chargerId, connectorId, une synthèse sûre du tarif/de la devise, createdAt et updatedAt.

Points de terminaison des profils de recharge

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/admin/charging-profileschargingProfiles:readCorps/paramètres de requête : aucun200 { items: ChargingProfile[], lookups { locations, purposes, kinds, recurrencyKinds, rateUnits, allowedRateUnits } }
POST /v2/admin/charging-profileschargingProfiles:createCorps : ChargingProfileCreate201 { profile: ChargingProfile }
GET /v2/admin/charging-profiles/:profileIdchargingProfiles:readCorps/paramètres de requête : aucun200 { profile: ChargingProfile, lookups, isEditBlocked: boolean, usage { simpleChargingSchedule, smartSchedule, chargePoolChargepoint, isUsed } }
PATCH /v2/admin/charging-profiles/:profileIdchargingProfiles:updateCorps : ChargingProfileInput complet200 { profile: ChargingProfile, ocppSync: OcppCommandResult[] }
PUT /v2/admin/charging-profiles/:profileId/schedulechargingProfiles:updateCorps : ChargingScheduleInput200 { profile: ChargingProfile, schedule: SchedulePeriod[], ocppSync: OcppCommandResult[] }
DELETE /v2/admin/charging-profiles/:profileIdchargingProfiles:deleteCorps : aucun204 empty uniquement après acceptation de l’effacement par toutes les bornes attribuées et nouvelle validation de l’état ; sinon 409 et le profil reste
GET /v2/admin/chargers/:chargerId/profile-assignmentschargers:profile-assignments + chargingProfiles:readCorps/paramètres de requête : aucun200 { items: ProfileAssignment[], profiles: ChargingProfile[] }
POST /v2/admin/chargers/:chargerId/profile-assignmentschargers:profile-assignments + chargingProfiles:assignCorps : ProfileAssignmentInput201 { assignment: ProfileAssignment, ocpp: OcppCommandResult }
PATCH /v2/admin/chargers/:chargerId/profile-assignments/:assignmentIdchargers:profile-assignments + chargingProfiles:assignCorps : ProfileAssignmentInput200 { assignment: ProfileAssignment, ocpp: OcppCommandResult }
DELETE /v2/admin/chargers/:chargerId/profile-assignments/:assignmentIdchargers:profile-assignments + chargingProfiles:assignCorps : aucun204 empty après effacement synchronisé

ProfileAssignment contient son ID d’attribution, les ID de borne/connecteur et une synthèse sûre du profil. Les objets de résultat OCPP exposent des informations stables de requête/statut et jamais d’identifiants de connexion.

Points de terminaison des utilisateurs, RFID, accès, limites et utilisateurs locaux

Méthode et cheminPortéeRequêteRéponse réussie
GET /v2/admin/usersusers:readParamètres : cursor? adapté au statut (members : numérique positif ; invitations : i2.* opaque authentifié ; all : continuation numérique de l’ancien flux de membres ou a2.* composite opaque authentifié), limit?=50 (1..100), status?="all", "members", or "invitations" ; en-tête X-Admin-User-Search facultatif (100 max.)200 { items: User[], invitations: Invitation[], chargers: ChargerSummary[], page { nextCursor, memberNextCursor, invitationNextCursor, limit } } ; les invitations sont paginées et enrichies avec la même expression d’e-mail normalisé afin que les anciennes différences de casse ou d’espacement ne masquent pas des lignes ; les curseurs sont liés à l’espace de travail et au filtre de recherche, ne contiennent pas d’e-mail lisible et doivent être renvoyés sans modification ; les curseurs all font avancer les flux de membres et d’invitations indépendamment
POST /v2/admin/usersusers:createCorps : UserCreate ; le client doit déjà être enregistré201 { user: User } ; un e-mail inconnu renvoie le corps 404 RESOURCE_NOT_FOUND sans révéler l’existence du compte et doit passer par les invitations
POST /v2/admin/users/invitationsusers:invitations + users:access ; plus users:rfid si rfid est fourniCorps : InvitationInput200 or 201 { result } ; le résultat de rattachement est { kind:"attached", user, accessChanged, rfidChanged } ; le résultat en attente est { kind:"invited", email, name, maskedRfid, chargerIds, createdCount, emailSent, emailRetryReady, alreadyInvited }
POST /v2/admin/users/invitations/resendusers:invitationsCorps : { email: email }200 { result: { targetedChargers, emailSent, emailRetryReady } } ; un délai commun à tous les envois au même destinataire est imposé
DELETE /v2/admin/users/invitationsusers:invitationsCorps : { email: email }204 empty
GET /v2/admin/users/:userIdusers:read + reports:readCorps/paramètres de requête : aucun200 { user: UserDetail, sessions: UserSession[], totals { count, energyWh, durationSeconds }, window, limitTypes, chargers } ; au maximum 200 sessions sur 90 jours
PATCH /v2/admin/users/:userIdusers:updateCorps : { name: string(1..255) }200 { user: User }
DELETE /v2/admin/users/:userIdusers:deleteCorps : aucun204 empty
GET /v2/admin/users/:userId/vehiclesusers:readParamètres : cursor? numérique, limit?=50 (1..100)200 { items: Vehicle[], page { nextCursor, limit } } ; le DTO du véhicule inclut des champs sûrs de fournisseur/statut/modèle/télémétrie
GET /v2/admin/users/:userId/rfidsusers:readCorps/paramètres de requête : aucun200 { items: RFID[] } ; les UID sont masqués
POST /v2/admin/users/:userId/rfidsusers:rfidCorps : { uid: string(1..36), name?: string or null }201 { rfid: RFID } ; l’UID brut n’est pas renvoyé dans la réponse
PATCH /v2/admin/users/:userId/rfids/:rfidIdusers:rfidCorps : { name: string or null }200 { rfid: RFID }
DELETE /v2/admin/users/:userId/rfids/:rfidIdusers:rfidCorps : aucun204 empty
GET /v2/admin/users/:userId/accessusers:readCorps/paramètres de requête : aucun200 { access: Access }
PUT /v2/admin/users/:userId/accessusers:accessCorps : AccessInput200 { access: Access }
DELETE /v2/admin/users/:userId/access/:accessIdusers:accessCorps : aucun204 empty
GET /v2/admin/users/:userId/limitsusers:readCorps/paramètres de requête : aucun200 { items: Limit[] }
POST /v2/admin/users/:userId/limitsusers:limitsCorps : LimitCreate201 { limit: Limit }
PATCH /v2/admin/users/:userId/limits/:limitIdusers:limitsCorps : LimitPatch, au moins une propriété200 { limit: Limit }
DELETE /v2/admin/users/:userId/limits/:limitIdusers:limitsCorps : aucun204 empty
GET /v2/admin/chargers/:chargerId/local-userschargers:read + users:readCorps/paramètres de requête : aucun200 { charger: ChargerSummary, items: LocalUser[], invitations: { email }[], availableMembers: [] } ; l’API omet volontairement le vaste répertoire des membres disponibles du portail
POST /v2/admin/chargers/:chargerId/local-userschargers:local-users + users:accessCorps : { customerId: positive integer, expiresAt?: RFC3339 or null }201 { localUser: LocalUserMutation }
POST /v2/admin/chargers/:chargerId/local-users/invitationschargers:local-users + users:invitations + users:accessCorps : { email: email }200 or 201 { result } ; client existant : { invited:false, localUser: LocalUserMutation } ; invitation : { invited:true, alreadyInvited, emailSent, emailRetryReady }
DELETE /v2/admin/chargers/:chargerId/local-users/invitationschargers:local-users + users:invitationsCorps : { email: email }204 empty
PATCH /v2/admin/chargers/:chargerId/local-users/:localUserIdchargers:local-users + users:accessCorps : { expiresAt: RFC3339 or null }200 { localUser: LocalUserUpdate }
DELETE /v2/admin/chargers/:chargerId/local-users/:localUserIdchargers:local-users + users:accessCorps : aucun204 empty

L’utilisateur système Tagless est protégé contre ces opérations. Les échecs d’envoi des invitations renvoient un état de livraison sûr sans divulguer les détails SMTP ; les appelants peuvent réessayer lorsque emailRetryReady vaut true.

Réponses binaires

Deux familles de points de terminaison ne renvoient pas de JSON en cas de réussite :

Point de terminaisonType de contenuEn-têtes importants
QR de borneimage/pngCache-Control: no-store, Content-Disposition: inline, X-Content-Type-Options: nosniff
Export de rapporttext/csv; charset=utf-8 ou application/pdfCache-Control: private, no-store, Content-Disposition: attachment, X-Content-Type-Options: nosniff

Les erreurs des points de terminaison binaires restent en JSON.