Customer Portal
Order history, trouble tickets, Smart Hands, work visits, shipments, cross connects, billing and reports.
Scope#
The Customer Portal domain covers the colocation operations APIs: the tasks otherwise done in the Equinix Customer Portal web interface.
| Accessor | Covers |
|---|---|
orderHistory() | Search across all order types and their status |
troubleTickets(), troubleTicketOrders() | Incident tickets |
smartHandsRequests() | Remote hands: twelve typed order kinds |
workVisits() | Site access for staff and visitors |
shipments() | Inbound and outbound shipments, pending storage |
crossConnects() | Cross connect order, update, deinstall |
secureCabinets(), orders(), quotes() | Other orders |
invoices(), billingAccounts(), billingAccountsSearch() | Billing |
reports() | Report Center: generate, schedule, download |
assets(), attachments(), lookups() | Asset search, attachments, locations and patch panels |
notifications(), unifiedNotifications() | Maintenance and incident notifications |
supportCases(), supportPlans() | Support |
digitalLoas() | Digital Letter of Authorization |
Order model#
An order create returns an order number and no resource body, and the v2 order APIs define no operation that reads the ordered resource. The status of an order is read from order history. The SDK therefore has no crossConnects().list() or workVisits().list(), and the read models that 3.0.0 offered for those resources are removed because no API operation returned them.
SDK method names carry no API version. Where a function needs both a v1 and a v2 API, the client calls both, and the version appears only in the request URL.
Order history#
import com.eqixiac.equinix.customerportal.enums.OrderHistoryProductType;
import com.eqixiac.equinix.customerportal.enums.OrderHistoryStatus;
import com.eqixiac.equinix.customerportal.model.OrderHistoryItem;
import com.eqixiac.equinix.customerportal.model.json.creators.OrderHistorySearchRequest;
OrderHistorySearchRequest request = OrderHistorySearchRequest.builder()
.filters(OrderHistorySearchRequest.Filters.builder()
.productTypes(OrderHistoryProductType.TROUBLE)
.orderStatus(List.of(OrderHistoryStatus.SUBMITTED, OrderHistoryStatus.IN_PROGRESS))
.ibxs(List.of("DC6", "DC11"))
.build())
.page(new OrderHistorySearchRequest.PageRequest(0, 50))
.build();
for (OrderHistoryItem order : portal.orderHistory().search(request)) {
System.out.println(order.getOrderNumber() + " " + order.getOrderStatus() + " " + order.getType()
+ " " + order.getIbx() + " created " + order.getCreatedAt());
}searchPage(request) returns the items with the paging block. The Tickets v2 API has no list operation, so tickets are listed through order history with product type TROUBLE. The live service rejects TROUBLE_TICKET with HTTP 400 EQX-171-1212.
The live service names several order-history fields differently from the specification. The SDK reads both forms: accountNumber and accountName, orderingContacts, and the contact fields firstName, lastName, emailAddress, workPhone. getNotificationContacts(), getTechnicalContacts() and getOrderSource() are new in 4.0.
Trouble tickets#
import com.eqixiac.equinix.customerportal.enums.TicketCode;
import com.eqixiac.equinix.customerportal.model.TroubleTicket;
import com.eqixiac.equinix.customerportal.model.json.creators.TicketNoteRequest;
import com.eqixiac.equinix.customerportal.model.json.creators.TroubleTicketCreateRequest;
String ticketId = portal.troubleTickets().create(
TroubleTicketCreateRequest.builder(
TicketCode.CODE_0001_0000, // problem code from the Tickets v2 catalogue
"Link down on cross connect 20012345-A since 02:14 UTC, no light received on our side",
"2026-11-12T02:14:00Z", // when the problem occurred
"DC6:01:012345") // primary identifier of the affected asset
.customerReferenceId("INC-48211")
.build());
TroubleTicket ticket = portal.troubleTickets().getByUuid(ticketId);
portal.troubleTickets().addNote(ticketId, new TicketNoteRequest("Our optic was replaced at 02:40 UTC, still no light"));The TicketCode constants are the numeric problem codes of the Tickets v2 specification, for example 0001-0000. The Equinix ticket documentation lists the problem category that each code stands for.
Work visits#
import com.eqixiac.equinix.customerportal.model.OrderResponse;
import com.eqixiac.equinix.customerportal.model.json.creators.WorkVisitCage;
import com.eqixiac.equinix.customerportal.model.json.creators.WorkVisitDetails;
import com.eqixiac.equinix.customerportal.model.json.creators.WorkVisitOrderRequest;
import com.eqixiac.equinix.customerportal.model.json.creators.WorkVisitVisitor;
WorkVisitDetails visit = WorkVisitDetails.builder(
List.of(new WorkVisitCage("DC6:01:012345").accountNumber("123456")),
"2026-11-20T14:00:00Z",
"2026-11-20T18:00:00Z",
List.of(WorkVisitVisitor.nonRegistered("Dana", "Okafor", "Example Networks")))
.openCabinet(true)
.build();
OrderResponse order = portal.workVisits().order(
WorkVisitOrderRequest.builder(visit)
.description("Replace failed line card, slot 3")
.customerReferenceId("CHG-10492")
.build());workVisits().listLocations() returns the IBXs, cages and cabinets the user may request access to, with the identifiers that the cage argument expects.
Smart Hands and shipments#
smartHandsRequests() has one create method per order kind: equipment install, shipment unpack, move or run jumper cable, cage escort, locate package, pictures and documentation, patch cable install and removal, cage cleanup, cable request and other. Each takes a SmartHandsRequestJson built from the IBX location, the contacts, the schedule and the service details for that kind. listTypes() and listLocations() return the valid kinds and locations.
shipments().orderInbound(...), orderOutbound(...) and orderPendingStorage(...) take typed requests for the three shipment variants. A Smart Hands or shipment order is billable, and the APIs define no dry run for either.
Billing#
import com.eqixiac.equinix.customerportal.model.InvoiceSummary;
Map<String, BigDecimal> totalByCurrency = new TreeMap<>();
for (InvoiceSummary invoice : portal.invoices().summaries().loadAll()) {
totalByCurrency.merge(invoice.getCurrencyCode().getCurrencyCode(), invoice.getTotalAmount(), BigDecimal::add);
}
totalByCurrency.forEach((currency, total) -> System.out.println(currency + " " + total));The block totals each currency separately. billingAccounts().downloadInvoiceDocument(...) returns the invoice document as bytes.
Reports#
import com.eqixiac.equinix.customerportal.enums.ReportStatus;
import com.eqixiac.equinix.customerportal.model.Report;
for (Report report : portal.reports().getReports(25, List.of(ReportStatus.SUCCESS), null)) {
byte[] file = portal.reports().downloadReport(report.getReportId());
Files.write(Path.of(report.getFileName()), file);
}generateReport(...) and scheduleReport(...) start report jobs, which is a side effect, so the read-only live tier does not call them.
Service behaviour observed live#
- Attachments, finance accounts, reports and asset search return their paging block as
page(Pagination). - Attachments answered 504 and asset search answered 500 with an empty body intermittently on 2026-11-11. The default policy retries both statuses for
GET. Asset search is aPOST, which the default policy does not retry on a 5xx (Retries). - The Digital LOA search requires a non-empty
filter.and. TheprivateBetaAccessandbetaTermsAgreementendpoints answered 404. - The order-history product-type enum on the live service also contains
INBOUND_SHIPMENTandOUTBOUND_SHIPMENT, which the specification lacks.
Worked example: colocation operations walkthrough.