Call an endpoint with curl
This page walks through a complete call from a terminal: get a token, call an endpoint with parameters, read the response, and recognise what each failure looks like. Every request and response below was captured from the demo workspace.
Before you start
You need:
- a published endpoint (here,
GET /orders/by-status); - a service account with an OAuth2 client, and its
client_idandclient_secret; - an invoke grant for that service account on the endpoint;
- the base URL of your deployment. On the Docker demo stack it is
http://egret.localtest.me:8080(or the port set inDEMO_PORT).
Set them as shell variables:
export BASE=http://egret.localtest.me:8080
export WS=default
export CLIENT_ID=egret-dataapi-cf9e7fe8-f970-45af-901d-66a9a24b9ab7
export CLIENT_SECRET='…the secret shown once when the client was issued…'
The Try it tab prints the exact curl command for a call, with a $TOKEN placeholder. Copy it from there. See Try it.
Get a token
Egret uses the OAuth2 client_credentials grant. Post the credentials as a form to /oauth/token.
curl -s -X POST "$BASE/oauth/token" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 600
}
The token is valid for 600 seconds. There is no refresh token: when it expires, request another with the same credentials. A client library should cache the token and renew it a little before expires_in.
Keep the token in a variable:
TOKEN=$(curl -s -X POST "$BASE/oauth/token" \
-d grant_type=client_credentials -d client_id=$CLIENT_ID -d client_secret=$CLIENT_SECRET \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
The token endpoint accepts a form body with client_id and client_secret fields. It does not accept HTTP Basic authentication or a JSON body: client_credentials requires client_id and client_secret and Form requests must have Content-Type: application/x-www-form-urlencoded are the respective answers. A wrong secret, an unknown client, a revoked or expired client all answer 401 {"error":"invalid_client"}.
Call an endpoint
The URL is {base}/data-api/v1/{workspace}/{path}, where {path} is the endpoint's Path without the leading slash.
curl -s "$BASE/data-api/v1/$WS/orders/by-status" \
-H "Authorization: Bearer $TOKEN"
{
"status": "ok",
"message": null,
"data": [
{ "status": "F", "revenue": 1.03568102349e+9, "order_count": 7304 },
{ "status": "O", "revenue": 1.02837633121e+9, "order_count": 7333 },
{ "status": "P", "revenue": 6.333947532e+7, "order_count": 363 }
],
"meta": {
"version": 2,
"cached": false,
"request_id": "2fcb3aad-5c85-4d10-a4cb-03a675ccd6ad",
"row_count": 3,
"columns": [
{ "name": "status", "ty": "varchar(1)", "required": true },
{ "name": "revenue", "ty": "double", "required": true },
{ "name": "order_count", "ty": "bigint", "required": true }
]
}
}
The same call a second time within the endpoint's cache TTL returns "cached": true.
Pass parameters
Parameters go in the query string, for GET and POST endpoints alike. A request body is not read.
curl -s "$BASE/data-api/v1/$WS/orders/by-status?priority=2-HIGH&min_total=100000" \
-H "Authorization: Bearer $TOKEN"
Rules to remember (see Query with parameters for the full list):
- Percent-encode values.
+decodes to a space; send a literal plus as%2B. - Repeat a key to send a list:
?statuses=O&statuses=P. - Keys the endpoint does not declare are ignored. An empty value (
?priority=) is not "absent" and is usually refused. - A
POSTendpoint is called withcurl -X POSTand the same query string. AGETrequest to aPOSTendpoint is404 not_found.
Several statements
An endpoint with several statements answers with an object keyed by statement name:
curl -s "$BASE/data-api/v1/$WS/orders/page?since=1998-06-01&after=5000000" \
-H "Authorization: Bearer $TOKEN"
{ "status": "ok", "message": null,
"data": { "rows": [], "total": [{ "total": 386 }] },
"meta": { "version": 1, "cached": false, "request_id": "d64e5b95-…", "statements": { "rows": { "row_count": 0 }, "total": { "row_count": 1 } } } }
Pin a version
Add -H "X-Egret-Version: 1" to keep calling an older frozen contract. See Publishing.
Read the service account's own contract
Without a path, the base URL returns an OpenAPI document listing exactly the endpoints this token's service account may invoke:
curl -s "$BASE/data-api/v1/$WS" -H "Authorization: Bearer $TOKEN"
See OpenAPI export.
Errors
Every failure has the same body: status is "error", message is a sentence for people, and error.code is the stable value to branch on. The HTTP status mirrors it. Quote error.request_id in a support request: it matches a row in Activity.
Missing or bad token: 401 key_invalid
curl -s -i "$BASE/data-api/v1/$WS/orders/by-status"
{"status":"error","message":"authentication failed","error":{"code":"key_invalid","message":"authentication failed","request_id":"dc510e52-d59c-4209-8437-92c32778d96d"}}
No grant: 403 not_granted. A service account partner-sandbox with a valid token but no grant:
{"status":"error","message":"this service account is not granted access to this endpoint; ask the endpoint's owner to grant it","error":{"code":"not_granted","message":"this service account is not granted access to this endpoint; ask the endpoint's owner to grant it","request_id":"d8e6bbd3-4acb-4fb8-b69e-1aa3a88cd9cc"}}
Bad parameter: 400 param_invalid. ?priority=9-X where priority is an enum:
{"status":"error","message":"a parameter value is not valid for this endpoint","error":{"code":"param_invalid","message":"a parameter value is not valid for this endpoint","request_id":"6a534646-c135-4720-a469-319ab4d65dad"}}
The message does not name the parameter, on purpose.
Missing required parameter: 400 param_missing, unknown path: 404 not_found, version not available: 409 version_unavailable (X-Egret-Version: 7 when only versions 1 and 2 exist).
The complete list is in the error reference.
Rate limits
When an endpoint has a rate limit and a caller exceeds it, Egret answers 429 rate_limited with a Retry-After header in seconds:
HTTP/1.1 429 Too Many Requests
content-type: application/json
retry-after: 1
On the walk-through endpoint (Per caller (qps) 5, Burst 10), thirty simultaneous requests from one client produced 10 to 12 200 answers and the rest 429. A client should wait the Retry-After interval and retry, with a little jitter:
for i in 1 2 3; do
code=$(curl -s -o /tmp/out.json -D /tmp/headers -w '%{http_code}' \
"$BASE/data-api/v1/$WS/orders/by-status" -H "Authorization: Bearer $TOKEN")
[ "$code" != 429 ] && break
sleep "$(grep -i '^retry-after:' /tmp/headers | tr -dc '0-9')"
done
503 overloaded also carries Retry-After: Egret shed the request because it was at its concurrency limit. It clears by itself.
Limits and known issues
- Tokens last 600 seconds and cannot be refreshed.
- The token endpoint takes form-encoded credentials only.
- A request body larger than the serving surface accepts answers
413 payload_too_large.