API Reference
Energy Connect Public API v1 — RESTful JSON over HTTPS
Download the current OpenAPI contract · Explorer · Integration guide
Last updated
Quickstart
Base URL: https://api.energyconnecter.ai/v1/public
The API covers 135 endpoints across 17 resource groups: Orders (47), Pricing (14), Connections & Freight Rates (12), Invoices (12), Companies (9), Webhooks (8), Locations (7), Supplier Terminals (5), Tax Rates (5), API Keys (4), Allocations (3), Load Board (3), Payments (2), and one each for Customers, Suppliers, Product Taxonomy and Reference Data. The browser below is rendered client-side; for a static list of every operation see /llms-full.txt.
Authentication
Pass your API key either as an X-API-Key header or as an Authorization: Bearer token — both are accepted. Keys start with ec_live_ (production) or ec_test_ (sandbox: isolated test company). A test key never reads or changes live data, sends no email or SMS, is not billed and has its own monthly cap; once authenticated, its responses carry X-EC-Sandbox: true.
curl -H "X-API-Key: ec_live_your_key_here" \ https://api.energyconnecter.ai/v1/public/orders # equivalently, as a Bearer token: curl -H "Authorization: Bearer ec_live_your_key_here" \ https://api.energyconnecter.ai/v1/public/orders
Idempotency
Every write (POST, PUT, PATCH, DELETE) requires an Idempotency-Key header — 16–200 characters of A-Za-z0-9_-. A UUID works. Omit it and the call returns 400 IDEMPOTENCY_KEY_REQUIRED. The only exception is POST /pricing/freight-calculate, which computes and writes nothing.
Use a fresh key for every distinct request. Retrying with the same key and the same body replays the original response — marked Idempotency-Replayed: true — without re-running side effects such as dispatch SMS or email. Records are kept for 24 hours. Reusing a key with a different body returns 409 IDEMPOTENCY_KEY_CONFLICT; a key whose original request is still in flight returns 409 IDEMPOTENCY_IN_FLIGHT with Retry-After. A key whose request ended in a 5xx returns 409 IDEMPOTENCY_OUTCOME_UNKNOWN: the write may have completed, so check the resource rather than resubmitting. After a 504 REQUEST_TIMEOUT the key is held for the full 24 hours; after any other 5xx, for 60 seconds (Retry-After gives the time left), and then a retry runs. After a 4xx the key is free at once, so you can fix the request and retry it.
curl -X POST https://api.energyconnecter.ai/v1/public/delivery-tickets \
-H "X-API-Key: ec_live_your_key_here" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"bolNumbers":["1234567"]}'
Rate Limits
600 requests/min burst50,000 billable requests/month
RateLimit-* headers on every response; Retry-After on 429
Response Format
Success: { success: true, data: ... }
List: { success: true, data: [...], meta: { total, page, pageSize, totalPages, hasMore } }
Error: { success: false, error: { code, message } }
Pagination
Use ?page=1&pageSize=20 query params.
Max pageSize is 100 (500 for connections).
To sync only what changed, GET /orders, /invoices and /delivery-tickets take ?updatedSince=: walk with meta.nextCursor passed back as cursor until hasMore is false.
Errors
400 VALIDATION_ERROR • 401 INVALID_API_KEY403 INSUFFICIENT_SCOPE • 404 NOT_FOUND429 RATE_LIMIT_EXCEEDED (error.kind: burst = retry soon, quota = monthly cap, wait for resetAt)
Versioning & deprecation
Within /v1 the contract only grows. An integration built against it keeps working, unchanged, for the life of /v1.
What v1 guarantees
- No endpoint, field or enum value is ever removed or renamed in place. A renamed concept keeps its old key as a permanent alias (
gallons/unitbesidequantity/unitCode). - We may add endpoints, optional request fields, response fields and enum values. Ignore fields you don't recognise and tolerate unknown enum values.
- Also treated as breaking, so they do not happen in v1: changing a field's type or meaning, tightening validation of existing input, changing a default (sort, page size, filter), changing the status code for an existing situation, making an always-present field nullable, and adding a required request field.
When a breaking change is needed
It ships as /v2/public, running beside v1. v1 stays up at least 12 months after v2 is generally available, and at least 6 months after the last active v1 caller migrates. Retirement is announced, then dual-run, then scheduled and announced brownouts (short 410 windows), then 410 Gone pointing to the migration guide. Nothing is removed silently.
Notice periods
| Change | Notice |
|---|---|
| Additive change | Changelog entry |
| Rate limits, quotas, IP policy | 30 days |
| Deprecation of v1 | 12 months before sunset |
| Brownout windows | Listed in the deprecation notice; reminders 30 and 7 days before each |
| Emergency security fix | Direct contact, before or at rollout |
Notices go to the creator of each affected active key and to the company's primary contact, name a person to reply to, and are recorded in the changelog below. A fix for behavior that contradicted this documentation can ship within v1 and is listed there too.
Changelog
- — Order lines add
productCode(product master id, e.g. DSL-ULSD-2) andterminalCode(IRS terminal control number, as onGET /master-terminals). Exact matches only: null when uncertain, so fall back to the name. - — Allocations: rows add
brand("" = unbranded) andsupplierId(granting supplier; null on your own row), so rows sharing terminal, product and period differ. - — Idempotency: a write that ends in a
5xxno longer releases itsIdempotency-Key. A retry with the same key gets the new409 IDEMPOTENCY_OUTCOME_UNKNOWN: for 24 hours after a504 REQUEST_TIMEOUT, which can still complete, and for 60 seconds after any other5xx, then the retry runs. A retry used to run a timed-out write a second time. A4xxstill frees the key at once. - — Invoices, invoice amendments and payments: a company that is neither the issuer nor the payer gets
404, the same as an unknown id, instead of403. A payer reading an unissued draft by id also gets404. A party acting outside its role still gets403. - — Price imports: committing a batch creates new supplier terminals for unmapped terminal codes only when the key also holds
supplier_terminals:write. Withpricing:writealone, those rows are skipped and their codes are listed in the newunresolvedTerminalCodesresponse field. Map them with/pricing/imports/terminal-aliasesand import again. - — Locations: a schedule day marked open must carry both
openTimeandcloseTime. A closed day needs neither. - — Locations: an empty
contacts,schedulesorcompartmentsarray returns400instead of deleting every row. Omit the field to leave it unchanged; setisActive: falseto stand a site down. - —
PUTon orders, locations, companies and supplier terminals is genuinely partial: fields you don't send are left unchanged. Schema defaults used to overwrite them. - — Order lines accept any unit in the units-of-measure catalog and fractional quantities.
gallons/unitremain permanent aliases.