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 :
nonesignifie aucun corps de requête. UnDELETEdocumentant un corps JSON exige toujours ce corps.?indique une propriété ou un paramètre de requête facultatif.T | nullsignifie que la propriété est présente et peut être null.204 emptysignifie aucun corps de réponse.CursorPage<T>signifie{ items: T[], nextCursor: number|string|null }, sauf lorsque la ligne nomme explicitement un objetpageoupagination.- 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 :
| DTO | Champs |
|---|---|
Workspace | id, name, email, website, address { line1, line2, postalCode, city, state, countryId, countryCode, countryName }, createdAt, updatedAt |
Site | id, name, description, address, timezone { id, name }, latitude, longitude, chargerCount ; le détail ajoute chargers normalisé et l’état du connecteur |
GeocodeCandidate | placeId, displayName, latitude, longitude ; les identifiants du fournisseur et ses métadonnées spécifiques ne sont jamais renvoyés |
MetricsWindow | from, to, limites/nombres de sessions et totalsCapped le cas échéant |
Charger | id, 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 |
Connector | portId, connectorId, ID de référentiel de connecteur, maxVoltage, maxAmperage, maxElectricPower |
ReportSession | id, 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 |
ReportSchedule | id, 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 |
Tariff | id, cpoId, name, objets de type/site/devise/zone de marché, tax, fallbackPrice, price effectif, components, assignmentCount |
ChargingProfile | id, cpoId, label, description, stackLevel, objets d’emplacement/usage/type/récurrence/unité de limitation, validité, programmation, assignmentCount, horodatages |
User | id, 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. |
RFID | id, maskedUid, name, type, valid ; l’UID brut n’est jamais renvoyé |
Access | allChargers, chargerIds trié et items contenant les métadonnées d’accès/borne/site/expiration |
Limit | id, limitTypeId, label, energyValue, timeValue |
LocalUser | DTO 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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin | workspaces:read | Corps : aucun | 200 { workspace: Workspace } |
PATCH /v2/admin | workspaces:update | Corps : WorkspacePatch | 200 { workspace: Workspace } |
GET /v2/admin/dashboard | workspaces:read + reports:read | Corps/paramètres de requête : aucun | 200 { 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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin/sites | sites:read | Corps/paramètres de requête : aucun | 200 { items: Site[], metricsWindow: MetricsWindow, lookups { countries, timezones } } |
GET /v2/admin/sites/geocode | sites:read | Paramè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égion | 200 { 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/sites | sites:create | Corps : SiteInput | 201 { site: Site } |
GET /v2/admin/sites/:siteId | sites:read + reports:read | Corps/paramètres de requête : aucun | 200 { site: SiteDetail, recentSessions: SiteSession[], totals, metricsWindow } |
PATCH /v2/admin/sites/:siteId | sites:update | Corps : SiteInput complet | 200 { site: Site } |
DELETE /v2/admin/sites/:siteId | sites:delete | Corps : aucun | 204 empty ; un site non vide renvoie 409 SITE_NOT_EMPTY |
GET /v2/admin/sites/:siteId/power-management | sites:power-management | Corps/paramètres de requête : aucun | 200 { 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-management | sites:power-management | Corps : PowerConfigurationInput | 200 { 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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin/chargers | chargers:read | Corps/paramètres de requête : aucun | 200 { items: Charger[], metricsWindow: MetricsWindow, connectorLookups { formats, types, powerTypes } } |
POST /v2/admin/chargers | chargers:create | Corps : ChargerCreate ; l’identifiant est converti en majuscules | 201 { charger: Charger } ; le mot de passe n’est jamais renvoyé |
GET /v2/admin/chargers/:chargerId | chargers:read | Corps/paramètres de requête : aucun | 200 { charger: ChargerDetail } |
PATCH /v2/admin/chargers/:chargerId | chargers:update | Corps : ChargerPatch | 200 { charger: ChargerDetail } |
DELETE /v2/admin/chargers/:chargerId | chargers:delete | Corps : aucun | 204 empty |
PATCH /v2/admin/chargers/:chargerId/credentials | chargers:credentials | Corps : { password: string(0..40) } | 200 { success: true } |
PATCH /v2/admin/chargers/:chargerId/connectors/:portId | chargers:connector-settings | Corps : ConnectorPatch | 200 { connector: Connector } |
PATCH /v2/admin/chargers/:chargerId/public-listing | chargers:public | Corps : { isPublic: boolean } | 200 { isPublic: boolean } ; des conditions de préparation non remplies renvoient 409 CHARGER_CONFLICT |
GET /v2/admin/chargers/:chargerId/public-readiness | chargers:read | Corps/paramètres de requête : aucun | 200 { readiness: { ready, missingRequirements, connectorIds, missingTariffConnectorIds, ...commercialState } } |
GET /v2/admin/chargers/:chargerId/logs | chargers:read | Paramètres : cursor?, limit?=50 (1..200), from?, to?, action?, direction?, levelId? | 200 { items: RedactedOcppLog[], nextCursor: number or null } |
GET /v2/admin/chargers/:chargerId/sessions | chargers:read + reports:read | Paramè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/qr | chargers:read | Paramètres : connector?: integer[0,255] | 200 image/png binaire, 300 px, Content-Disposition: inline, Cache-Control: no-store |
GET /v2/admin/chargers/:chargerId/composite-schedules | chargers:read | Paramètres : cursor?, limit?=20 (1..50), connectorId? | 200 { items: CompositeSchedule[], nextCursor: number or null } |
GET /v2/admin/chargers/:chargerId/configuration | chargers:read | Corps/paramètres de requête : aucun | 200 { items: ChargerConfiguration[] } ; les valeurs assimilables à des secrets sont [REDACTED] |
PATCH /v2/admin/chargers/:chargerId/configuration | chargers:configuration | Corps : { 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/reload | chargers:configuration | Corps : aucun | 200 { status: string } |
GET /v2/admin/chargers/:chargerId/firmware | chargers:read | Corps/paramètres de requête : aucun | 200 { firmware: { status, notifiedAt, currentVersion, bootNotifiedAt } } |
POST /v2/admin/chargers/:chargerId/firmware | chargers:firmware | Corps : { url: HTTP-or-HTTPS URL, max 2048 characters } | 202 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/reset | chargers:commands | Corps : { resetType: "Soft" or "Hard" } | 200 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/availability | chargers:commands | Corps : { availabilityType: "Operative" or "Inoperative" } | 200 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/trigger | chargers:commands | Corps : { messageType: "BootNotification", "Heartbeat", "StatusNotification", or "MeterValues" } | 200 { status: string, connectorResults: { connectorId, status }[] } |
POST /v2/admin/chargers/:chargerId/commands/unlock | chargers:commands | Corps : { connectorId: positive integer } | 200 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/composite-schedule | chargers:commands | Corps : { 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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin/reports/data | reports:read | Paramètres : ReportFilters, cursor? opaque, limit?=50 (1..100) | 200 { items: ReportSession[], pageTotals, page { limit, nextCursor }, range { from, to, toExclusive: true } } |
GET /v2/admin/reports/schedule | reports:read | Corps/paramètres de requête : aucun | 200 { schedule: ReportSchedule or null } |
PUT /v2/admin/reports/schedule | reports:schedules | Corps : schéma strict ReportScheduleInput | 200 { schedule: ReportSchedule } |
POST /v2/admin/reports/send | reports:send | Corps : aucun ; l’en-tête Idempotency-Key est obligatoire | 200 { 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/measurements | reports:read | Paramè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/export | reports:export | Paramè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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin/tariffs | tariffs:read | Corps/paramètres de requête : aucun | 200 { items: Tariff[], lookups { currencies, biddingZones, currencyRates, componentTypes, seasons, tariffTypes } } |
POST /v2/admin/tariffs | tariffs:create | Corps : TariffInput | 201 { tariff: Tariff } |
GET /v2/admin/tariffs/:tariffId | tariffs:read | Corps/paramètres de requête : aucun | 200 { tariff: Tariff, lookups } |
PATCH /v2/admin/tariffs/:tariffId | tariffs:update | Corps : TariffInput complet | 200 { tariff: Tariff } |
PUT /v2/admin/tariffs/:tariffId/components | tariffs:components | Corps : { components: TariffComponentInput[0..256] } | 200 { tariff: Tariff, components: TariffComponent[] } |
DELETE /v2/admin/tariffs/:tariffId | tariffs:delete | Corps : aucun | 204 empty ; les références depuis des enregistrements de recharge renvoient 409 TARIFF_CONFLICT |
GET /v2/admin/tariffs/:tariffId/market-prices | tariffs:read | Paramètres : startsAt obligatoire, horodatage Unix non négatif | 200 { items: { id, value, currencyId, startsAt, endsAt, startsAtIso, endsAtIso, intervalMinutes }[], truncated: boolean } ; au maximum 10 000 lignes |
GET /v2/admin/chargers/:chargerId/tariff-assignments | chargers:tariff-assignments + tariffs:read | Corps/paramètres de requête : aucun | 200 { items: TariffAssignment[], tariffs: Tariff[] } |
POST /v2/admin/chargers/:chargerId/tariff-assignments | chargers:tariff-assignments + tariffs:assign | Corps : TariffAssignmentCreateInput ; omettre connectorId ou connectorId=0 signifie tous les connecteurs | 201 { assignments: TariffAssignment[] } |
PATCH /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentId | chargers:tariff-assignments + tariffs:assign | Corps : TariffAssignmentUpdateInput ; le connecteur est obligatoire et ne peut pas être 0 | 200 { assignment: TariffAssignment } |
DELETE /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentId | chargers:tariff-assignments + tariffs:assign | Corps : aucun | 204 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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin/charging-profiles | chargingProfiles:read | Corps/paramètres de requête : aucun | 200 { items: ChargingProfile[], lookups { locations, purposes, kinds, recurrencyKinds, rateUnits, allowedRateUnits } } |
POST /v2/admin/charging-profiles | chargingProfiles:create | Corps : ChargingProfileCreate | 201 { profile: ChargingProfile } |
GET /v2/admin/charging-profiles/:profileId | chargingProfiles:read | Corps/paramètres de requête : aucun | 200 { profile: ChargingProfile, lookups, isEditBlocked: boolean, usage { simpleChargingSchedule, smartSchedule, chargePoolChargepoint, isUsed } } |
PATCH /v2/admin/charging-profiles/:profileId | chargingProfiles:update | Corps : ChargingProfileInput complet | 200 { profile: ChargingProfile, ocppSync: OcppCommandResult[] } |
PUT /v2/admin/charging-profiles/:profileId/schedule | chargingProfiles:update | Corps : ChargingScheduleInput | 200 { profile: ChargingProfile, schedule: SchedulePeriod[], ocppSync: OcppCommandResult[] } |
DELETE /v2/admin/charging-profiles/:profileId | chargingProfiles:delete | Corps : aucun | 204 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-assignments | chargers:profile-assignments + chargingProfiles:read | Corps/paramètres de requête : aucun | 200 { items: ProfileAssignment[], profiles: ChargingProfile[] } |
POST /v2/admin/chargers/:chargerId/profile-assignments | chargers:profile-assignments + chargingProfiles:assign | Corps : ProfileAssignmentInput | 201 { assignment: ProfileAssignment, ocpp: OcppCommandResult } |
PATCH /v2/admin/chargers/:chargerId/profile-assignments/:assignmentId | chargers:profile-assignments + chargingProfiles:assign | Corps : ProfileAssignmentInput | 200 { assignment: ProfileAssignment, ocpp: OcppCommandResult } |
DELETE /v2/admin/chargers/:chargerId/profile-assignments/:assignmentId | chargers:profile-assignments + chargingProfiles:assign | Corps : aucun | 204 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 chemin | Portée | Requête | Réponse réussie |
|---|---|---|---|
GET /v2/admin/users | users:read | Paramè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/users | users:create | Corps : 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/invitations | users:invitations + users:access ; plus users:rfid si rfid est fourni | Corps : InvitationInput | 200 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/resend | users:invitations | Corps : { email: email } | 200 { result: { targetedChargers, emailSent, emailRetryReady } } ; un délai commun à tous les envois au même destinataire est imposé |
DELETE /v2/admin/users/invitations | users:invitations | Corps : { email: email } | 204 empty |
GET /v2/admin/users/:userId | users:read + reports:read | Corps/paramètres de requête : aucun | 200 { user: UserDetail, sessions: UserSession[], totals { count, energyWh, durationSeconds }, window, limitTypes, chargers } ; au maximum 200 sessions sur 90 jours |
PATCH /v2/admin/users/:userId | users:update | Corps : { name: string(1..255) } | 200 { user: User } |
DELETE /v2/admin/users/:userId | users:delete | Corps : aucun | 204 empty |
GET /v2/admin/users/:userId/vehicles | users:read | Paramè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/rfids | users:read | Corps/paramètres de requête : aucun | 200 { items: RFID[] } ; les UID sont masqués |
POST /v2/admin/users/:userId/rfids | users:rfid | Corps : { 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/:rfidId | users:rfid | Corps : { name: string or null } | 200 { rfid: RFID } |
DELETE /v2/admin/users/:userId/rfids/:rfidId | users:rfid | Corps : aucun | 204 empty |
GET /v2/admin/users/:userId/access | users:read | Corps/paramètres de requête : aucun | 200 { access: Access } |
PUT /v2/admin/users/:userId/access | users:access | Corps : AccessInput | 200 { access: Access } |
DELETE /v2/admin/users/:userId/access/:accessId | users:access | Corps : aucun | 204 empty |
GET /v2/admin/users/:userId/limits | users:read | Corps/paramètres de requête : aucun | 200 { items: Limit[] } |
POST /v2/admin/users/:userId/limits | users:limits | Corps : LimitCreate | 201 { limit: Limit } |
PATCH /v2/admin/users/:userId/limits/:limitId | users:limits | Corps : LimitPatch, au moins une propriété | 200 { limit: Limit } |
DELETE /v2/admin/users/:userId/limits/:limitId | users:limits | Corps : aucun | 204 empty |
GET /v2/admin/chargers/:chargerId/local-users | chargers:read + users:read | Corps/paramètres de requête : aucun | 200 { 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-users | chargers:local-users + users:access | Corps : { customerId: positive integer, expiresAt?: RFC3339 or null } | 201 { localUser: LocalUserMutation } |
POST /v2/admin/chargers/:chargerId/local-users/invitations | chargers:local-users + users:invitations + users:access | Corps : { 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/invitations | chargers:local-users + users:invitations | Corps : { email: email } | 204 empty |
PATCH /v2/admin/chargers/:chargerId/local-users/:localUserId | chargers:local-users + users:access | Corps : { expiresAt: RFC3339 or null } | 200 { localUser: LocalUserUpdate } |
DELETE /v2/admin/chargers/:chargerId/local-users/:localUserId | chargers:local-users + users:access | Corps : aucun | 204 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 terminaison | Type de contenu | En-têtes importants |
|---|---|---|
| QR de borne | image/png | Cache-Control: no-store, Content-Disposition: inline, X-Content-Type-Options: nosniff |
| Export de rapport | text/csv; charset=utf-8 ou application/pdf | Cache-Control: private, no-store, Content-Disposition: attachment, X-Content-Type-Options: nosniff |
Les erreurs des points de terminaison binaires restent en JSON.