# Connect ERP lookup: NetSuite, Odoo, JD Edwards

One Amazon Connect Lambda handler, three adapters, one flat string map. Companion to
[Amazon Connect ERP Integration: NetSuite, Odoo and JD Edwards](/blog/amazon-connect-erp-integration-netsuite-odoo-jd-edwards-2026/).

Node 24, ES modules, no npm dependencies (global `fetch`, `node:crypto`).

## Run the tests

```bash
cd examples/architecture-blog-2026/connect-commerce-context/erp
node --test
```

All HTTP is mocked. Nothing here has been run against a live NetSuite, Odoo or JD Edwards system.

## Output contract

Every key and value is a string matching `[A-Za-z0-9_-]` (AWS documents that character set for the Lambda response, under 64 KB).

| Key | Meaning |
| --- | --- |
| `lookupStatus` | `OK` or `FALLBACK` |
| `fallbackReason` | only on fallback: `TIMEOUT`, `HTTP_<status>` (e.g. `HTTP_401`, `HTTP_503`), `AMBIGUOUS` (two customers share the phone), `BAD_RESPONSE` (HTTP 200 without the expected fields), `BAD_INPUT`, `NOT_CONFIGURED` |
| `accountStatus` | `ACTIVE`, `INACTIVE`, `NOT_FOUND`, `UNKNOWN` |
| `openInvoiceCount` | count, or `UNKNOWN` |
| `lastOrderStatus`, `lastOrderRef` | ERP order state and reference, `NONE`, or `UNKNOWN` |

Branch the flow on `lookupStatus` with Check contact attributes. Never read the other keys when it is `FALLBACK`.

The handler never throws, so Lambda's `Errors` metric stays at zero. It writes one JSON log line per call (`evt`, `erp`, `lookupStatus`, `fallbackReason`, `ms`; never the phone or ERP data). Alarm on a CloudWatch metric filter over that line.

## Configuration (environment, for the sample only)

| Backend | Variables |
| --- | --- |
| all | `ERP_BACKEND` (or flow parameter `erp`), `ERP_BUDGET_MS` (default 5000) |
| odoo | `ODOO_URL`, `ODOO_API_KEY`, `ODOO_DB` (optional) |
| netsuite | `NS_BASE` (`https://<account>.suitetalk.api.netsuite.com`), `NS_CLIENT_ID`, `NS_CERT_ID`, `NS_PRIVATE_KEY_PEM`, `NS_JWT_ALG` (default `PS256`, unverified) |
| jde | `JDE_AIS_URL`, `JDE_ORCHESTRATION`, `JDE_USER`, `JDE_PASSWORD` |

In production, read these from AWS Secrets Manager at cold start and cache them. Do not store the private key or API key as plain Lambda environment variables.

## Unverified

- NetSuite JWT algorithm, `kid`/claims and token endpoint path: written from the client credentials flow's general shape. Check Oracle's current page for your account before relying on it.
- NetSuite status codes (`CustInvc:A` stored as `A`) and the `isinactive` value: confirm in your account.
- Odoo field names (`payment_state`, `move_type`, `state`) are standard model fields. Confirm for your version and custom modules.
- JD Edwards: you author the orchestration. Its input and output names here are this sample's convention.
