Cereri și răspunsuri ale endpointurilor API-ului de administrare a spațiului de lucru
Aceasta este referința cererilor/răspunsurilor pentru fiecare endpoint /v2/admin. Autentificarea, idempotența, limitele de rată, crearea tokenurilor și semantica domeniilor de acces sunt documentate în API-ul de administrare a spațiului de lucru. Tipurile reutilizabile la nivel de câmp și exemplele se află în Scheme și exemple. Citește fiecare rând de endpoint împreună cu Contractul de răspunsuri și erori, care enumeră toate corpurile posibile de răspuns de nereușită comune și specifice endpointurilor.
Contract comun
Fiecare cale începe cu originea implementată app-api. Spațiul de lucru este derivat exclusiv din tokenul autentificat; clienții nu trebuie să trimită cpoId. Toate valorile documentate ale căilor de resurse :...Id sunt numere întregi zecimale pozitive. Fiecare cerere necesită exact unul:
Authorization: Bearer <workspace-access-token>
Cererile afișate cu un corp JSON necesită Content-Type: application/json. Toate modificările necesită Idempotency-Key (POST, PUT, PATCH și DELETE), inclusiv POST /v2/admin/reports/send; dacă un rând nu precizează altfel, tipul de conținut al succesului este application/json, datele JSON sunt șiruri RFC 3339, iar numerele sunt numere JSON.
Fiecare cerere poate include X-Request-Id și X-Error-Format: envelope; vezi Contractul de răspunsuri și erori. Aceste antete nu modifică schemele de succes.
Notația folosită mai jos:
noneînseamnă fără corp de cerere. UnDELETEcare documentează un corp JSON necesită în continuare acel corp.?marchează o proprietate sau un parametru de interogare opțional.T | nullînseamnă că proprietatea este prezentă și poate fi null.204 emptyînseamnă fără corp de răspuns.CursorPage<T>înseamnă{ items: T[], nextCursor: number|string|null }, cu excepția cazului în care rândul numește explicit un obiectpagesaupagination.- Toate cazurile de resurse imbricate negăsite, inclusiv ID-uri din alte spații de lucru, folosesc
404 { "error": "Workspace resource not found", "code": "RESOURCE_NOT_FOUND" }, cu excepția funcțiilor care documentează un conflict mai specific.
Scheme comune de cerere
Date de intrare pentru spații de lucru și locații
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
}
Date de intrare pentru gestionarea puterii
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
}
Date de intrare pentru stații
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]
}
Date de intrare pentru rapoarte
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"
}
Intervalele rapoartelor trebuie să respecte from < to și nu pot depăși 90 de zile. Când sunt omise, este folosit ultimul interval de 90 de zile. userId și anonymous=true se exclud reciproc. Exporturile programate recuperează o minută UTC ratată și folosesc reîncercări limitate (întârziere de 5/10 minute, rezervare de 30 de minute pentru o revendicare expirată, trei încercări).
Date de intrare pentru tarife
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 }
Date de intrare pentru profiluri de încărcare
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 }
Date de intrare pentru utilizatori
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 }
Cel puțin unul dintre energyValue sau timeValue trebuie să fie mai mare decât zero la crearea unei limite. Spațiile de la capetele adreselor de email sunt eliminate, iar adresele sunt convertite în litere mici. UID-urile RFID sunt acceptate doar la scriere și sunt mascate în răspunsurile de citire.
Rezumatul DTO-urilor de răspuns
Tabelele endpointurilor afișează fiecare anvelopă de răspuns de nivel superior. Acestea sunt principalele DTO-uri reutilizabile din acele anvelope:
| DTO | Câmpuri |
|---|---|
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; detaliul adaugă chargers normalizat și starea conectorului |
GeocodeCandidate | placeId, displayName, latitude, longitude; datele de autentificare și metadatele specifice furnizorului nu sunt returnate niciodată |
MetricsWindow | from, to, limite/număr de sesiuni și totalsCapped, unde este cazul |
Charger | id, name, identifier, cpoId, site, status normalizat, isPublic, indicatori RFID/inteligent/automatizare, provisioned, connection, evses, connectors, createdAt, updatedAt; detaliul adaugă configuration redactat, firmware și numărători |
Connector | portId, connectorId, ID-uri de referință ale conectorului, maxVoltage, maxAmperage, maxElectricPower |
ReportSession | id, stație/locație, ID-uri de port și conector, utilizator minimizat, active, energyWh, început/sfârșit/durată și costuri nete/taxe/totale per monedă |
ReportSchedule | id, workspaceId, enabled, recipientEmail, format, sendDay/sendTime UTC, metadate de creare/actualizare și metadate sigure despre ultima livrare, inclusiv numărul încercărilor |
Tariff | id, cpoId, name, obiecte de tip/locație/monedă/zonă de ofertare, tax, fallbackPrice, price efectiv, components, assignmentCount |
ChargingProfile | id, cpoId, label, description, stackLevel, obiecte de locație/scop/tip/recurență/unitate de rată, valabilitate, program, assignmentCount, marcaje temporale |
User | id, membershipId, name, email, rfids mascat, access al stației, limitCount; detaliul adaugă phone, createdAt și limits. Datele de autorizare OCPP, precum contractId, nu sunt returnate niciodată. |
RFID | id, maskedUid, name, type, valid; UID-ul brut nu este returnat niciodată |
Access | allChargers, chargerIds sortat și items care conține metadate de permisiune/stație/locație/expirare |
Limit | id, limitTypeId, label, energyValue, timeValue |
LocalUser | DTO de listă: id, customerId, name, email, expiresAt, systemManaged. Modificările utilizatorilor locali folosesc structurile distincte LocalUserMutation/LocalUserUpdate din Scheme și exemple. |
Obiectele de referință sunt returnate de rutele de listă/detaliu, astfel încât apelanții să poată descoperi ID-uri valide. Tratează câmpurile noi necunoscute ca fiind compatibile cu versiunile viitoare și diferențiază cazurile doar pe baza câmpurilor stabile și codurilor de eroare documentate.
Endpointuri pentru spații de lucru
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin | workspaces:read | Corp: fără | 200 { workspace: Workspace } |
PATCH /v2/admin | workspaces:update | Corp: WorkspacePatch | 200 { workspace: Workspace } |
GET /v2/admin/dashboard | workspaces:read + reports:read | Corp/interogare: fără | 200 { window, stats { chargerCount, onlineChargerCount, siteCount, userCount }, sessions: DashboardSession[], analytics }; sesiunile sunt limitate la 1.000 pe 90 de zile |
Endpointuri pentru locații și gestionarea puterii
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin/sites | sites:read | Corp/interogare: fără | 200 { items: Site[], metricsWindow: MetricsWindow, lookups { countries, timezones } } |
GET /v2/admin/sites/geocode | sites:read | Interogare: opțional street (max. 160), city, state, country (max. 100 fiecare), countryCode (max. 2); antet opțional Accept-Language; sunt necesare cel puțin două caractere de stradă/oraș/stat cumulate | 200 { results: GeocodeCandidate[] }; căutările identice aflate în curs sunt reunite, iar activitatea furnizorului folosește o coadă serializată limitată și un cache limitat pe server; căutările nevalide returnează 400 INVALID_GEOCODE_QUERY, saturarea cozii returnează 429 GEOCODING_BUSY cu Retry-After, iar erorile furnizorului returnează 502 GEOCODING_UNAVAILABLE |
POST /v2/admin/sites | sites:create | Corp: SiteInput | 201 { site: Site } |
GET /v2/admin/sites/:siteId | sites:read + reports:read | Corp/interogare: fără | 200 { site: SiteDetail, recentSessions: SiteSession[], totals, metricsWindow } |
PATCH /v2/admin/sites/:siteId | sites:update | Corp: SiteInput complet | 200 { site: Site } |
DELETE /v2/admin/sites/:siteId | sites:delete | Corp: fără | 204 empty; o locație care nu este goală returnează 409 SITE_NOT_EMPTY |
GET /v2/admin/sites/:siteId/power-management | sites:power-management | Corp/interogare: fără | 200 { configuration: PowerConfiguration } care conține locația deținută, stații/conectori, grupuri, surse de contor, atribuiri și starea compatibilității |
PUT /v2/admin/sites/:siteId/power-management | sites:power-management | Corp: PowerConfigurationInput | 200 { configuration: PowerConfiguration }; refuzul OCPP, expirarea sau o modificare concurentă returnează 409 POWER_MANAGEMENT_CONFLICT fără a aplica înlocuirea |
Endpointuri pentru stații
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin/chargers | chargers:read | Corp/interogare: fără | 200 { items: Charger[], metricsWindow: MetricsWindow, connectorLookups { formats, types, powerTypes } } |
POST /v2/admin/chargers | chargers:create | Corp: ChargerCreate; identificatorul este convertit în majuscule | 201 { charger: Charger }; parola nu este returnată niciodată |
GET /v2/admin/chargers/:chargerId | chargers:read | Corp/interogare: fără | 200 { charger: ChargerDetail } |
PATCH /v2/admin/chargers/:chargerId | chargers:update | Corp: ChargerPatch | 200 { charger: ChargerDetail } |
DELETE /v2/admin/chargers/:chargerId | chargers:delete | Corp: fără | 204 empty |
PATCH /v2/admin/chargers/:chargerId/credentials | chargers:credentials | Corp: { password: string(0..40) } | 200 { success: true } |
PATCH /v2/admin/chargers/:chargerId/connectors/:portId | chargers:connector-settings | Corp: ConnectorPatch | 200 { connector: Connector } |
PATCH /v2/admin/chargers/:chargerId/public-listing | chargers:public | Corp: { isPublic: boolean } | 200 { isPublic: boolean }; cerințele de pregătire neîndeplinite returnează 409 CHARGER_CONFLICT |
GET /v2/admin/chargers/:chargerId/public-readiness | chargers:read | Corp/interogare: fără | 200 { readiness: { ready, missingRequirements, connectorIds, missingTariffConnectorIds, ...commercialState } } |
GET /v2/admin/chargers/:chargerId/logs | chargers:read | Interogare: 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 | Interogare: 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 | Interogare: connector?: integer[0,255] | 200 image/png binar, 300 px, Content-Disposition: inline, Cache-Control: no-store |
GET /v2/admin/chargers/:chargerId/composite-schedules | chargers:read | Interogare: cursor?, limit?=20 (1..50), connectorId? | 200 { items: CompositeSchedule[], nextCursor: number or null } |
GET /v2/admin/chargers/:chargerId/configuration | chargers:read | Corp/interogare: fără | 200 { items: ChargerConfiguration[] }; valorile asemănătoare secretelor sunt [REDACTED] |
PATCH /v2/admin/chargers/:chargerId/configuration | chargers:configuration | Corp: { key: string(1..50), value: string(0..500) } | 200 { status: string } unde starea este rezultatul stației/OCPP |
POST /v2/admin/chargers/:chargerId/configuration/reload | chargers:configuration | Corp: fără | 200 { status: string } |
GET /v2/admin/chargers/:chargerId/firmware | chargers:read | Corp/interogare: fără | 200 { firmware: { status, notifiedAt, currentVersion, bootNotifiedAt } } |
POST /v2/admin/chargers/:chargerId/firmware | chargers:firmware | Corp: { url: HTTP-or-HTTPS URL, max 2048 characters } | 202 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/reset | chargers:commands | Corp: { resetType: "Soft" or "Hard" } | 200 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/availability | chargers:commands | Corp: { availabilityType: "Operative" or "Inoperative" } | 200 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/trigger | chargers:commands | Corp: { messageType: "BootNotification", "Heartbeat", "StatusNotification", or "MeterValues" } | 200 { status: string, connectorResults: { connectorId, status }[] } |
POST /v2/admin/chargers/:chargerId/commands/unlock | chargers:commands | Corp: { connectorId: positive integer } | 200 { status: string } |
POST /v2/admin/chargers/:chargerId/commands/composite-schedule | chargers:commands | Corp: { connectorId: nonnegative integer, durationSeconds?: integer[60,604800], chargingRateUnit?: "W" or "A" } | 200 { status: string, connectorId: number, compositeSchedule: CompositeSchedule or null } |
Endpointuri pentru rapoarte
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin/reports/data | reports:read | Interogare: ReportFilters, cursor? opac, limit?=50 (1..100) | 200 { items: ReportSession[], pageTotals, page { limit, nextCursor }, range { from, to, toExclusive: true } } |
GET /v2/admin/reports/schedule | reports:read | Corp/interogare: fără | 200 { schedule: ReportSchedule or null } |
PUT /v2/admin/reports/schedule | reports:schedules | Corp: ReportScheduleInput strict | 200 { schedule: ReportSchedule } |
POST /v2/admin/reports/send | reports:send | Corp: fără; antetul Idempotency-Key este obligatoriu | 200 { sent: true, format: "csv" or "pdf", rowCount: number, range { from, to } }; lipsa unui program salvat returnează 409 REPORT_SCHEDULE_REQUIRED |
GET /v2/admin/reports/measurements | reports:read | Interogare: transactionLogId obligatoriu, cursor? opac, limit?=500 (1..1000) | 200 { transactionLogId, measurements: { name, unit, lines[] }[], page { limit, nextCursor, sampleValuesTruncated, sampleLimitPerMeterValue: 64 } } |
GET /v2/admin/reports/export | reports:export | Interogare: ReportFilters, format?="csv" or "pdf" | 200 binar CSV/PDF cu numele fișierului atașat, Cache-Control: private, no-store, X-Content-Type-Options: nosniff; peste 2.000 de rânduri returnează 422 REPORT_EXPORT_ROW_LIMIT |
Endpointuri pentru tarife
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin/tariffs | tariffs:read | Corp/interogare: fără | 200 { items: Tariff[], lookups { currencies, biddingZones, currencyRates, componentTypes, seasons, tariffTypes } } |
POST /v2/admin/tariffs | tariffs:create | Corp: TariffInput | 201 { tariff: Tariff } |
GET /v2/admin/tariffs/:tariffId | tariffs:read | Corp/interogare: fără | 200 { tariff: Tariff, lookups } |
PATCH /v2/admin/tariffs/:tariffId | tariffs:update | Corp: TariffInput complet | 200 { tariff: Tariff } |
PUT /v2/admin/tariffs/:tariffId/components | tariffs:components | Corp: { components: TariffComponentInput[0..256] } | 200 { tariff: Tariff, components: TariffComponent[] } |
DELETE /v2/admin/tariffs/:tariffId | tariffs:delete | Corp: fără | 204 empty; referințele înregistrărilor de încărcare returnează 409 TARIFF_CONFLICT |
GET /v2/admin/tariffs/:tariffId/market-prices | tariffs:read | Interogare: startsAt obligatoriu, marcaj temporal Unix nenegativ | 200 { items: { id, value, currencyId, startsAt, endsAt, startsAtIso, endsAtIso, intervalMinutes }[], truncated: boolean }; cel mult 10.000 de rânduri |
GET /v2/admin/chargers/:chargerId/tariff-assignments | chargers:tariff-assignments + tariffs:read | Corp/interogare: fără | 200 { items: TariffAssignment[], tariffs: Tariff[] } |
POST /v2/admin/chargers/:chargerId/tariff-assignments | chargers:tariff-assignments + tariffs:assign | Corp: TariffAssignmentCreateInput; omiterea lui connectorId sau connectorId=0 înseamnă toți conectorii | 201 { assignments: TariffAssignment[] } |
PATCH /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentId | chargers:tariff-assignments + tariffs:assign | Corp: TariffAssignmentUpdateInput; conectorul este obligatoriu și nu poate fi 0 | 200 { assignment: TariffAssignment } |
DELETE /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentId | chargers:tariff-assignments + tariffs:assign | Corp: fără | 204 empty |
TariffAssignment conține id, chargerId, connectorId, un rezumat sigur al tarifului/monedei, createdAt și updatedAt.
Endpointuri pentru profiluri de încărcare
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin/charging-profiles | chargingProfiles:read | Corp/interogare: fără | 200 { items: ChargingProfile[], lookups { locations, purposes, kinds, recurrencyKinds, rateUnits, allowedRateUnits } } |
POST /v2/admin/charging-profiles | chargingProfiles:create | Corp: ChargingProfileCreate | 201 { profile: ChargingProfile } |
GET /v2/admin/charging-profiles/:profileId | chargingProfiles:read | Corp/interogare: fără | 200 { profile: ChargingProfile, lookups, isEditBlocked: boolean, usage { simpleChargingSchedule, smartSchedule, chargePoolChargepoint, isUsed } } |
PATCH /v2/admin/charging-profiles/:profileId | chargingProfiles:update | Corp: ChargingProfileInput complet | 200 { profile: ChargingProfile, ocppSync: OcppCommandResult[] } |
PUT /v2/admin/charging-profiles/:profileId/schedule | chargingProfiles:update | Corp: ChargingScheduleInput | 200 { profile: ChargingProfile, schedule: SchedulePeriod[], ocppSync: OcppCommandResult[] } |
DELETE /v2/admin/charging-profiles/:profileId | chargingProfiles:delete | Corp: fără | 204 empty doar după ce toate stațiile atribuite acceptă ștergerea și starea este revalidată; altfel 409, iar profilul rămâne |
GET /v2/admin/chargers/:chargerId/profile-assignments | chargers:profile-assignments + chargingProfiles:read | Corp/interogare: fără | 200 { items: ProfileAssignment[], profiles: ChargingProfile[] } |
POST /v2/admin/chargers/:chargerId/profile-assignments | chargers:profile-assignments + chargingProfiles:assign | Corp: ProfileAssignmentInput | 201 { assignment: ProfileAssignment, ocpp: OcppCommandResult } |
PATCH /v2/admin/chargers/:chargerId/profile-assignments/:assignmentId | chargers:profile-assignments + chargingProfiles:assign | Corp: ProfileAssignmentInput | 200 { assignment: ProfileAssignment, ocpp: OcppCommandResult } |
DELETE /v2/admin/chargers/:chargerId/profile-assignments/:assignmentId | chargers:profile-assignments + chargingProfiles:assign | Corp: fără | 204 empty după ștergerea sincronizată |
ProfileAssignment conține ID-ul atribuirii, ID-urile stației/conectorului și un rezumat sigur al profilului. Obiectele de rezultat OCPP expun informații stabile despre cerere/stare și niciodată date de autentificare.
Endpointuri pentru utilizatori, RFID, acces, limite și utilizatori locali
| Metodă și cale | Domeniu de acces | Cerere | Răspuns de succes |
|---|---|---|---|
GET /v2/admin/users | users:read | Interogare: cursor? corespunzător stării (members: numeric pozitiv; invitations: i2.* opac autentificat; all: continuare numerică de membru veche sau a2.* compozit opac autentificat), limit?=50 (1..100), status?="all", "members", or "invitations"; antet opțional X-Admin-User-Search (max. 100) | 200 { items: User[], invitations: Invitation[], chargers: ChargerSummary[], page { nextCursor, memberNextCursor, invitationNextCursor, limit } }; invitațiile sunt paginate și completate folosind aceeași expresie normalizată de email, astfel încât literele mari/mici sau spațiile vechi să nu poată ascunde rânduri; cursoarele sunt legate de spațiul de lucru și filtrul de căutare, nu conțin adrese de email lizibile și trebuie returnate nemodificate; cursoarele all avansează independent fluxurile de membri și invitații |
POST /v2/admin/users | users:create | Corp: UserCreate; clientul trebuie să fie deja înregistrat | 201 { user: User }; o adresă de email necunoscută returnează corpul 404 RESOURCE_NOT_FOUND care nu permite enumerarea și ar trebui să folosească invitații |
POST /v2/admin/users/invitations | users:invitations + users:access; plus users:rfid dacă este furnizat rfid | Corp: InvitationInput | 200 or 201 { result }; rezultatul asocierii este { kind:"attached", user, accessChanged, rfidChanged }; rezultatul în așteptare este { kind:"invited", email, name, maskedRfid, chargerIds, createdCount, emailSent, emailRetryReady, alreadyInvited } |
POST /v2/admin/users/invitations/resend | users:invitations | Corp: { email: email } | 200 { result: { targetedChargers, emailSent, emailRetryReady } }; este aplicat intervalul de așteptare global al destinatarului |
DELETE /v2/admin/users/invitations | users:invitations | Corp: { email: email } | 204 empty |
GET /v2/admin/users/:userId | users:read + reports:read | Corp/interogare: fără | 200 { user: UserDetail, sessions: UserSession[], totals { count, energyWh, durationSeconds }, window, limitTypes, chargers }; cel mult 200 de sesiuni pe 90 de zile |
PATCH /v2/admin/users/:userId | users:update | Corp: { name: string(1..255) } | 200 { user: User } |
DELETE /v2/admin/users/:userId | users:delete | Corp: fără | 204 empty |
GET /v2/admin/users/:userId/vehicles | users:read | Interogare: cursor? numeric, limit?=50 (1..100) | 200 { items: Vehicle[], page { nextCursor, limit } }; DTO-ul vehiculului include câmpuri sigure de furnizor/stare/model/telemetrie |
GET /v2/admin/users/:userId/rfids | users:read | Corp/interogare: fără | 200 { items: RFID[] }; UID-urile sunt mascate |
POST /v2/admin/users/:userId/rfids | users:rfid | Corp: { uid: string(1..36), name?: string or null } | 201 { rfid: RFID }; UID-ul brut nu este repetat |
PATCH /v2/admin/users/:userId/rfids/:rfidId | users:rfid | Corp: { name: string or null } | 200 { rfid: RFID } |
DELETE /v2/admin/users/:userId/rfids/:rfidId | users:rfid | Corp: fără | 204 empty |
GET /v2/admin/users/:userId/access | users:read | Corp/interogare: fără | 200 { access: Access } |
PUT /v2/admin/users/:userId/access | users:access | Corp: AccessInput | 200 { access: Access } |
DELETE /v2/admin/users/:userId/access/:accessId | users:access | Corp: fără | 204 empty |
GET /v2/admin/users/:userId/limits | users:read | Corp/interogare: fără | 200 { items: Limit[] } |
POST /v2/admin/users/:userId/limits | users:limits | Corp: LimitCreate | 201 { limit: Limit } |
PATCH /v2/admin/users/:userId/limits/:limitId | users:limits | Corp: LimitPatch, cel puțin o proprietate | 200 { limit: Limit } |
DELETE /v2/admin/users/:userId/limits/:limitId | users:limits | Corp: fără | 204 empty |
GET /v2/admin/chargers/:chargerId/local-users | chargers:read + users:read | Corp/interogare: fără | 200 { charger: ChargerSummary, items: LocalUser[], invitations: { email }[], availableMembers: [] }; API-ul omite intenționat directorul larg de membri disponibili al portalului |
POST /v2/admin/chargers/:chargerId/local-users | chargers:local-users + users:access | Corp: { 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 | Corp: { email: email } | 200 or 201 { result }; client existent: { invited:false, localUser: LocalUserMutation }; invitație: { invited:true, alreadyInvited, emailSent, emailRetryReady } |
DELETE /v2/admin/chargers/:chargerId/local-users/invitations | chargers:local-users + users:invitations | Corp: { email: email } | 204 empty |
PATCH /v2/admin/chargers/:chargerId/local-users/:localUserId | chargers:local-users + users:access | Corp: { expiresAt: RFC3339 or null } | 200 { localUser: LocalUserUpdate } |
DELETE /v2/admin/chargers/:chargerId/local-users/:localUserId | chargers:local-users + users:access | Corp: fără | 204 empty |
Utilizatorul de sistem Tagless este protejat de aceste operațiuni. Eșecurile trimiterii emailurilor de invitație returnează o stare sigură a livrării fără a divulga detalii SMTP; apelanții pot reîncerca atunci când emailRetryReady este adevărat.
Răspunsuri binare
Două familii de endpointuri nu returnează JSON în caz de succes:
| Endpoint | Tip de conținut | Antete importante |
|---|---|---|
| QR al stației | image/png | Cache-Control: no-store, Content-Disposition: inline, X-Content-Type-Options: nosniff |
| Export de rapoarte | text/csv; charset=utf-8 sau application/pdf | Cache-Control: private, no-store, Content-Disposition: attachment, X-Content-Type-Options: nosniff |
Erorile endpointurilor binare sunt în continuare JSON.