01 / The problem
The expected payment exists before the transaction does.
Imagine an invoice or agent workflow that already knows the expected sender, receiver, USDC amount, and optionally an invoice note. The payer has not sent the transfer yet. There is therefore no txId to confirm.
Your application now needs to observe future Algorand rounds, find a candidate asset-transfer transaction, apply the exact business predicate, and remember enough progress to continue after a restart. That is a different problem from checking the status of a transaction you already know.
RoundWatch models that expectation as a durable watch instead of tying it to one caller process.
02 / Why txId polling is different
Algod confirmation starts after you already know the transaction.
Algorand's algod client exposes pendingTransactionInformation(txId) for a recently submitted transaction. That is the right primitive when the transaction ID is already known. It cannot discover a future transfer whose ID does not exist yet.
03 / Build it with Indexer
The Indexer gives you search primitives, not your payment lifecycle.
Algorand Indexer can search transactions by address and role, asset ID, round range, note prefix, transaction type, and amount bounds. That makes it a strong foundation for discovering candidates.
GET https://YOUR_INDEXER/v2/transactions
?address=EXPECTED_RECEIVER
&address-role=receiver
&asset-id=31566704
&tx-type=axfer
&min-round=START_ROUNDA practical implementation can query by receiver, MainNet USDC asset, transaction type, and the last safely processed round. It then post-filters candidates against the full payment contract.
// Pseudocode: the Indexer narrows the search; your app still owns exact matching.
for (const tx of response.transactions) {
if (
senderMatches(tx, expectedSender) &&
receiverMatches(tx, expectedReceiver) &&
assetMatches(tx, 31566704) &&
amountMatches(tx, atomicAmount) &&
noteMatches(tx, invoiceNote)
) {
persistEvidence(tx);
}
}Algorand Indexer docs: searchForTransactionsWhat your application still has to own
- the expected sender and receiver
- the fixed asset identity
- the exact atomic amount
- the optional invoice-note predicate
- a safe starting round and scan cursor
- pagination and retry behavior
- restart recovery
- idempotency for repeated watch creation
- terminal result persistence
04 / Production failure modes
The loop is easy. The durable state machine is the real work.
The process dies between rounds.
Without a persisted cursor, the next process must guess where to resume and can either rescan excessively or leave a gap.
The candidate set spans multiple pages.
A durable scanner has to advance only after it knows the relevant result window was processed safely.
The receiver got a different USDC transfer.
Receiver-only matching is insufficient. Sender, asset, amount, and the optional note all belong to the payment predicate.
The caller retries watch creation.
Idempotency matters because a network retry should not create two independent obligations for the same invoice.
The payment appears near or after a deadline.
Expiry should be based on complete indexed coverage of the closing range, not merely on wall-clock time passing.
The match must survive after detection.
Store the matching transaction evidence so a later workflow can retrieve the same result instead of rediscovering it.
05 / Build vs delegate
You can own the worker, or you can own only the payment intent.
Build it yourself
- Create a persistent job.
- Capture the starting round.
- Poll and paginate Indexer results.
- Persist cursor progress.
- Recover safely after restarts.
- Apply the exact payment predicate.
- Persist the terminal evidence.
Delegate to RoundWatch
- Define the exact expected payment.
- Create one durable watch.
- Save the returned watch ID.
- Let the caller exit.
- Read the durable result later.
RoundWatch does not replace Algorand Indexer. It packages the long-lived observation, cursor persistence, restart recovery, exact-match predicate, and stored result into a bounded API obligation.
POST /v1/watch
content-type: application/json
{
"idempotencyKey": "invoice-2026-09-22-001",
"expectedSender": "EXPECTED_SENDER",
"expectedReceiver": "EXPECTED_RECEIVER",
"atomicAmount": "1000000",
"invoiceNote": "roundwatch:invoice-2026-09-22-001"
}06 / Retrieve evidence
Your workflow can return after the waiting period.
After durable watch creation succeeds, keep the watchId. The caller can terminate and later query the public watch status endpoint. A matched watch preserves the exact Algorand transaction ID and confirmed round that satisfied the watch.
GET /v1/watch/YOUR_WATCH_ID
// Return later. The caller does not need to keep its own chain-watching
// process alive between watch creation and status retrieval.The current operating contract, states, capacity limits, and MainNet integration steps are documented on the quickstart page rather than duplicated here.
07 / When RoundWatch fits
Use the abstraction only when it removes work you actually have.
RoundWatch is useful when…
- the transaction does not exist yet
- you know the expected payment fields in advance
- your caller should be free to exit
- restart-safe observation would otherwise be your job
- you need durable evidence after the match
You probably do not need it when…
- you already know the txId and only need confirmation
- you already run a durable Indexer ingestion pipeline
- your backend already owns persistent queues and recovery
- you require push webhook delivery rather than later retrieval
08 / FAQ
Common implementation questions.
Why not just call pendingTransactionInformation()?
That API takes a transaction ID. A future invoice payment has no transaction ID until the payer creates and submits the transfer.
Does RoundWatch replace Algorand Indexer?
No. Indexer is the chain-data search layer. RoundWatch is the durable payment-observation lifecycle built on top of chain data.
Is RoundWatch a webhook service?
No. The current product persists the watch and its result; the client retrieves status later. It solves the durable observation problem that often sits underneath a webhook integration.
Why match more than receiver and amount?
Because an unrelated transfer can share a receiver or amount. Exact payment intent is stronger when sender, receiver, fixed asset, amount, and an optional invoice note all agree.
From explanation to integration
Create the watch. Let RoundWatch own the wait.
The quickstart contains the current MainNet request flow, operating limits, states, and reference implementation.
Start in 60 seconds