Use the transaction HTTP API
Read records, prepare changes and commit them together on supported instances.
A transaction lets you prepare several row changes before committing them. This preview HTTP API uses a tx_id to keep those operations together.
- Begin and keep tx_idUse the returned transaction ID for its operations.
- Read supported keysStay within the endpoint’s documented read behavior.
- Buffer supported writesInspect errors before attempting to finish the transaction.
- Commit or roll backVerify the final result; a buffered write is not yet committed.
Choose the interface
Use this page for /v1/tenants/:tenant/tx/*. For SQL statements using a shared session, use SQL transactions instead. They are separate interfaces with different limits.
Before you begin
- Confirm the instance supports the requested mode in
/v1/capabilities. - Complete the quickstart, including the
product-001record inquickstart.products. - Use the engine connection values expected by this example:
OC_BASE_URL,OC_TENANTandOC_BEARER.
Read, change, commit
Python example: increase a sample price by 100 cents
import json
import os
from urllib.request import Request, urlopen
base = os.environ["OC_BASE_URL"].rstrip("/")
tenant = os.environ["OC_TENANT"]
headers = {"Authorization": f"Bearer {os.environ['OC_BEARER']}",
"Content-Type": "application/json"}
def call(method, path, body=None):
request = Request(f"{base}/v1/tenants/{tenant}{path}", method=method,
headers=headers,
data=None if body is None else json.dumps(body).encode())
with urlopen(request, timeout=30) as response:
return json.load(response)
opened = call("POST", "/tx/begin?isolation=snapshot_isolation")
tx_id = opened["tx_id"]
print("Transaction:", tx_id, "isolation:", opened["isolation"])
try:
if opened["isolation"] != "snapshot_isolation":
raise RuntimeError("Unexpected isolation level")
current = call("GET", f"/tx/{tx_id}/rows/quickstart.products/product-001")["row"]
call("POST", f"/tx/{tx_id}/rows/quickstart.products", {
"id": current["id"], "name": current["name"],
"price_cents": current["price_cents"] + 100,
})
except Exception:
try:
call("POST", f"/tx/{tx_id}/rollback")
except Exception:
pass
raise
# A lost commit response needs reconciliation, not an unconditional retry.
print(call("POST", f"/tx/{tx_id}/commit"))Transaction reads return a row wrapper and see their own buffered writes. Other clients see changes after a successful commit.
Know what each mode checks
| Mode | What to expect |
|---|---|
snapshot_isolation | Checks recorded point reads and conflicting writes at commit. A second read is not guaranteed to match the first. |
serializable | Preview; requires a supported single-shard configuration. Read the begin receipt's scope. |
On supported single-writer instances, serializable validation can cover supported scan predicates. On RaftQuorum, serializable_scope: point_reads_only excludes scan and range reads. A lagging replica does not guarantee the most recent value acknowledged elsewhere.
Read several rows
POST /tx/:id/query takes a supported native scan plan, not SQL text. Unsupported plan leaves are refused. If you found keys outside the transaction, read those keys again inside it before deciding what to change.
SQL sessions have their own buffering and isolation restrictions. Do not use their higher-isolation INSERT support as proof that this HTTP workflow is available on every instance.
Handle failures
| Outcome | Next step |
|---|---|
Confirmed tx_conflict or serialization_failure | Start a fresh transaction, re-read and recalculate. Bound the number of retries. |
| Lost commit response or timeout | Check whether the change happened before repeating it. |
| Constraint or permission error | Correct the request or permission; repeating it is not a fix. |
Buffer and open-transaction limits have their own error codes. serializable_sharded_unsupported and serializable_scan_raft_unsupported mean the requested access pattern is unavailable. HTTP 409 alone is not enough to decide whether to retry.
Keep the transaction small
Keep transactions short, handle expiry and check the final commit. Constraints can fail at commit even when individual buffered writes were accepted. Schema changes use a separate lifecycle. Trigger external side effects only after the transaction's outcome is confirmed.