API mocks
On this page
NetSuite
A mock compatible with Oracle NetSuite's SuiteTalk REST web services. Your integration creates and reads customers, items, sales orders and invoices, runs SuiteQL queries and authenticates with token-based authentication (OAuth 1.0a) or OAuth 2.0 machine-to-machine, as it does against a NetSuite account, with NetSuite's paths, status codes and error format. Each mock is one empty account. Records and other objects the mock keeps follow your plan's retention (3 days on Free) and are then deleted, so recreate fixtures in your tests rather than relying on them; see API mocks.
Testlie is not affiliated with, endorsed by or sponsored by Oracle or NetSuite. These names are trademarks of their owners, used here only to say which API this mock is compatible with.
Connect
Create a NetSuite mock under API mocks → Business tools. Use its base URL in place of https://<account>.suitetalk.api.netsuite.com. Paths are the same: /services/rest/record/v1/... and /services/rest/query/v1/suiteql.
Token-based authentication (OAuth 1.0a)
The mock's account id (the realm), consumer key and secret, and token id and secret work like an integration record and an access token in NetSuite. Sign each request with HMAC-SHA256:
Authorization: OAuth realm="TSTDRV1234567", oauth_consumer_key="…", oauth_token="…", oauth_signature_method="HMAC-SHA256", oauth_timestamp="…", oauth_nonce="…", oauth_version="1.0", oauth_signature="…"The signature base string is built from the URL you call (the mock's base URL plus the path) and its query parameters, as OAuth 1.0a specifies. The mock checks the realm, keys, signature, a timestamp within 5 minutes and that a nonce is not reused. Anything wrong answers NetSuite's 401 INVALID_LOGIN. Libraries such as oauth-1.0a work unchanged.
OAuth 2.0 client credentials (M2M)
The mock also has a client id, a certificate id and the certificate's private key (in NetSuite you upload the certificate; here the mock generated the key pair). Post a JWT assertion signed with the private key:
POST /services/rest/auth/oauth2/v1/token
grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=<JWT>The JWT header has alg PS256 (or RS256), typ: "JWT" and kid = the certificate id; its claims are iss = the client id, scope (["restlets", "rest_webservices"] or a string; it must include rest_webservices), aud = the token URL, iat and exp (at most one hour later). The answer is { "access_token", "expires_in": "3600", "token_type": "bearer" }; use Authorization: Bearer <token>. Errors are 400 { "error": "invalid_client" | "invalid_grant" | "invalid_scope" | "invalid_request" }.
Records
Record types: customer, contact, vendor, employee, inventoryItem, nonInventorySaleItem, serviceSaleItem, salesOrder, invoice, customerPayment and journalEntry.
| Endpoint | |
|---|---|
POST /services/rest/record/v1/{type} |
Create: 204 with the new record's URL in Location |
GET /services/rest/record/v1/{type}/{id} |
Read, with links; ?fields=a,b limits the fields |
PATCH /services/rest/record/v1/{type}/{id} |
Update the fields sent: 204 |
PUT /services/rest/record/v1/{type}/eid:{externalId} |
Upsert by external id: 204 |
DELETE /services/rest/record/v1/{type}/{id} |
Delete: 204 |
GET /services/rest/record/v1/{type} |
List: { links, count, hasMore, items: [{ links, id }], offset, totalResults } with limit (up to 1000) and offset (a multiple of limit) |
{id} can also be eid:<externalId>. The q parameter of lists filters like NetSuite's record filters, for example q=email CONTAIN "example.com" AND creditLimit GREATER 1000 (operators IS, IS_NOT, CONTAIN, CONTAIN_NOT, START_WITH, END_WITH, EMPTY, EMPTY_NOT, EQUAL, GREATER, LESS, ANY_OF, ...).
Fields are stored as you send them. References are objects with an id, for example "entity": { "id": "107" }. The mock checks the fields NetSuite requires: companyName for a company customer or vendor, firstName and lastName for a person, a contact or an employee, itemId for items, and entity and at least one item.items line for sales orders and invoices. It fills in entityId, and for transactions tranId, tranDate, total and status.
SuiteQL
POST /services/rest/query/v1/suiteql?limit=100&offset=0
Prefer: transient
{ "q": "SELECT id, companyname, email FROM customer WHERE email LIKE '%@example.com' ORDER BY id" }The Prefer: transient header is required, as in NetSuite. The answer has the same shape as record lists, with rows in items (lower-case column names, values as strings, empty values left out). Tables: customer, contact, vendor, employee, item and transaction (sales orders, invoices, payments and journal entries, with type SalesOrd, CustInvc, CustPymt, Journal).
The mock understands SELECT [TOP n] columns, *, COUNT(*) and AS aliases, FROM one table (with an alias), WHERE with =, <>, !=, <, >, <=, >=, LIKE, IN, IS [NOT] NULL, AND, OR, NOT and parentheses, and ORDER BY. Joins, GROUP BY and SQL functions answer 400 INVALID_SEARCH.
Errors
Errors use NetSuite's format:
{ "type": "https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.1", "title": "Bad Request", "status": 400,
"o:errorDetails": [{ "detail": "Error while accessing a resource. Please enter value(s) for: Company Name.", "o:errorPath": "companyName", "o:errorCode": "USER_ERROR" }] }Codes: INVALID_LOGIN (401), USER_ERROR, INVALID_CONTENT, INVALID_PARAMETER, INVALID_HEADER, INVALID_SEARCH, INVALID_URL (400), NONEXISTENT_ID (404), INVALID_METHOD (405). The 429 rate limit scenario answers CONCURRENCY_LIMIT_EXCEEDED.
Not supported
SOAP web services, RESTlets, sublists as separate resources, expandSubResources, saved searches, record actions (transform, etc.), metadata catalog, asynchronous requests and the authorization code flow are not mocked. The mock does not check that referenced records exist.