Retries without doing it twice
A change that fails in transit can be sent again safely: GrowLocal recognises the request by its operationId and answers with the receipt it already has instead of making the change a second time.
Send the same request again
Give every new change a new operationId. If you don't get an answer, or get a 5xx, send the same body again with the same operationId. A change GrowLocal already made is not made again: you get its receipt.
# Run it twice: the second answer is the first receipt, and only one row is added.
curl https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/invocations \
-H "Authorization: Bearer gl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target": {
"featureId": "YOUR_FEATURE_ID",
"export": "mutateCollection",
"contractRef": "data.collection.mutate@1"
},
"operationId": "quote-request-jordan-1",
"input": {
"resourceRef": "YOUR_TABLE_REF",
"mutation": {
"kind": "create",
"values": { "f_name": "Jordan Example", "f_email": "jordan@example.com" }
}
}
}'
// Retry the same body, with the same operationId, until an answer arrives.
const body = JSON.stringify({
target: {
featureId: "YOUR_FEATURE_ID",
export: "mutateCollection",
contractRef: "data.collection.mutate@1",
},
operationId: "quote-request-jordan-1",
input: {
resourceRef: "YOUR_TABLE_REF",
mutation: {
kind: "create",
values: { f_name: "Jordan Example", f_email: "jordan@example.com" },
},
},
});
for (let attempt = 1; attempt <= 3; attempt += 1) {
try {
const response = await fetch(
"https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/invocations",
{
method: "POST",
headers: {
Authorization: "Bearer gl_YOUR_API_KEY",
"Content-Type": "application/json",
},
body,
},
);
if (response.status < 500) {
console.log(response.status, await response.json());
break;
}
} catch (error) {
console.log(`attempt ${attempt} failed: ${error}`);
}
}
import requests
# Retry the same body, with the same operationId, until an answer arrives.
body = {
"target": {
"featureId": "YOUR_FEATURE_ID",
"export": "mutateCollection",
"contractRef": "data.collection.mutate@1",
},
"operationId": "quote-request-jordan-1",
"input": {
"resourceRef": "YOUR_TABLE_REF",
"mutation": {
"kind": "create",
"values": {"f_name": "Jordan Example", "f_email": "jordan@example.com"},
},
},
}
for attempt in range(1, 4):
try:
response = requests.post(
"https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/invocations",
headers={"Authorization": "Bearer gl_YOUR_API_KEY"},
json=body,
timeout=30,
)
except requests.RequestException as error:
print(f"attempt {attempt} failed: {error}")
continue
if response.status_code < 500:
print(response.status_code, response.json())
break
The same operationId with a different body is refused: a conflict receipt whose diagnostic is DATA.OPERATION_REPLAY_MISMATCH, "this operation identity is already bound to other inputs". An id belongs to one request.
Look a change up
To find out what became of a change without sending it again:
GET /operations/{operationId}
answers that change's receipt, or 404 if this key has made no change with that id. Use it for a pending or uncertain receipt too: look again until the status settles.
curl https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/operations/quote-request-jordan-1 \
-H "Authorization: Bearer gl_YOUR_API_KEY"
const response = await fetch(
"https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/operations/quote-request-jordan-1",
{ headers: { Authorization: "Bearer gl_YOUR_API_KEY" } },
);
console.log(response.status, await response.json());
import requests
response = requests.get(
"https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/operations/quote-request-jordan-1",
headers={"Authorization": "Bearer gl_YOUR_API_KEY"},
timeout=30,
)
print(response.status_code, response.json())
Operation ids are kept per key: another key's change with the same id is a different change, and this key can't look it up.
Reads aren't kept
A read changes nothing, so its receipt isn't kept: GET /operations/{operationId} answers 404 for it. Send a read again whenever you need it.
Changing a row someone else changed
A change to an existing row carries the expectedRevision you read it at. If the row has changed since, the answer is a conflict receipt with DATA.REVISION_CONFLICT ("row is at revision …"), and nothing is overwritten. Read the row again, decide, and send the change with its new revision and a new operationId.