This runbook documents the procedures for manually replaying ledger events and email queue items in the YieldVault platform. Manual replays are typically performed during recovery from database restoration, RPC node sync lag, queue delivery failure, or to fix data inconsistencies.
Stellar blockchain event ingestion is handled automatically by the EventPollingService. If the service is restarted, it automatically replays missed events from the last recorded cursor sequence to the current network ledger.
However, if there is a data corruption event or database restore, operators can trigger a manual range replay.
- Admin API Key: You must have an API key with the
adminorsuper-adminrole. - Ledger Sequence Numbers: You need to know the target
fromLedgerandtoLedgersequences. - Maximum Range Limit: By default, manual replays are limited to
1000ledgers per request to prevent overloading (configured viaEVENT_REPLAY_MAX_RANGE_SIZE).
Always perform a dry-run first to validate your request schema and preview the range boundaries.
curl -X POST http://localhost:3000/admin/events/replay \
-H "Authorization: ApiKey YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fromLedger": 152000,
"toLedger": 152500,
"dryRun": true
}'Expected Response (200 OK):
{
"dryRun": true,
"message": "Event replay dry-run preview",
"fromLedger": 152000,
"toLedger": 152500,
"ledgerCount": 501,
"wouldReplay": true,
"timestamp": "2026-06-26T18:00:00.000Z"
}Omit the dryRun flag or set it to false to execute the actual database updates and event reprocessing.
curl -X POST http://localhost:3000/admin/events/replay \
-H "Authorization: ApiKey YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fromLedger": 152000,
"toLedger": 152500
}'Expected Response (200 OK):
{
"message": "Event replay completed successfully",
"fromLedger": 152000,
"toLedger": 152500,
"processedCount": 12,
"duplicateCount": 489
}Note: processedCount tracks newly processed events; duplicateCount tracks events already processed and skipped due to idempotency verification.
- Monitor Server Logs: Check the server logs for processed event logs:
tail -n 100 /var/log/yieldvault/backend.log | grep -i "Event processed"
- Verify in Database: Query the database using
psqlto check the updated events cursor and processed events count:-- Verify the current cursor SELECT * FROM "EventCursor" WHERE id = 1; -- Check recently processed events in range SELECT COUNT(*), "eventType" FROM "ProcessedEvent" WHERE "ledgerSeq" >= 152000 AND "ledgerSeq" <= 152500 GROUP BY "eventType";
- API_400_EVENT_REPLAY (HTTP 400 Bad Request): Occurs if
fromLedger>toLedger, values are negative, or parameters are missing. - Ledger Range Too Large: If you exceed the maximum allowed size (e.g. 1000 ledgers), split the request into smaller chunks (e.g., 152000–152499, and 152500–152999).
When the system fails to deliver system notifications (such as deposit confirmations), failed jobs are placed in a queue. If an email fails repeatedly, it can be manually replayed.
- Admin API Key: Authorization via
Authorization: ApiKey <api-key>. - Email ID: The UUID of the failed email record in the database.
Find the IDs of failed emails that need recovery:
# Get failed emails via local query
psql $DATABASE_URL -c "SELECT id, recipient, subject, status, \"retryCount\" FROM \"EmailQueue\" WHERE status = 'failed' LIMIT 10;"Send a POST request to the replay endpoint using the specific email ID:
curl -X POST http://localhost:3000/admin/emails/replay/YOUR_EMAIL_UUID \
-H "Authorization: ApiKey YOUR_ADMIN_API_KEY"Expected Response (200 OK):
{
"message": "Email requeued successfully",
"email": {
"id": "YOUR_EMAIL_UUID",
"status": "pending",
"retryCount": 0,
"lastError": null
}
}Check the queue worker status or query the database to ensure the email was picked up and sent:
SELECT status, "retryCount", "lastError" FROM "EmailQueue" WHERE id = 'YOUR_EMAIL_UUID';If successfully sent, the status changes to sent.