---
title: "Read DRE protocol logs into audit actions (cron)"
method: POST
path: "/cron/protocol-logs"
tags: ["cron"]
---

# Read DRE protocol logs into audit actions (cron)

`POST /cron/protocol-logs`

## Request

- Base URL: `https://api.dre.app` (the document declares no server; this is the origin it was published from)
- URL: `https://api.dre.app/cron/protocol-logs`
- Auth: none declared. Required headers: `X-CRON-API-KEY`
- TypeScript: <https://skmtc.dev/dre/apis/dre-api/docs/cron/protocol-logs/post.ts> (a function with typed inputs; its response is checked against the schema)

Expected schedule: **every 5 minutes**.

Reads `dreUSDManager` events from Base and USDC transfers into the reserves wallet from **both Base and Ethereum**, and writes the matching `audit_actions` rows. Replaces the `dre-indexer` feed for the audit table (DRE-467, DRE-549).

A transfer is income because a **named payer sent a named token on a named chain** (`RESERVE_FLOW_RULES`), not because its sender failed an exclusion list. Anything matching no rule is filed in `unclassified_reserve_inflows` and alerted, never booked (DRE-550).

Reads only up to each chain’s **finalized** block: an audit row has no removal path, and `AuditActionSubscriber` moves `collaterals` on insert, so a reorged log would leave the published collateral ratio wrong with nothing to correct it. Finality costs ~23 minutes on Base and ~16 on Ethereum; that lag is deliberate.

A cursor per `(chain, stream)` advances only after every log in a block is written, so a crash or deploy replays that block; the partial unique index on `(chain_id, tx_hash, log_index)` makes the replay a no-op. A Base cursor is never moved below the block the DRE-466 backfill reached, because rows below it carry no `log_index` and cannot be deduped by log. That mark is a **Base** block number and is never applied to another chain.

Disabled unless `PROTOCOL_POLL_ENABLED=true`. The contracts are constants and the chains are a fixed plan, so that flag plus `PROTOCOL_POLL_START_BLOCK` and `PROTOCOL_POLL_START_BLOCK_ETHEREUM` are the whole configuration surface. Pass `?dryRun=true` to compare against existing rows without writing — it reports both missing rows and rows whose values disagree. Returns the per-stream report, each entry carrying its `chainId`, cursor lag and `unclassified` count. Requires `X-CRON-API-KEY` matching `CRON_API_KEY`.

## Query parameters

- `dryRun` string, required

## Headers

- `X-CRON-API-KEY` string, required

## Response `200`

Per-stream poll report

## Other responses

- `401`: Invalid cron API key

## Changes

- **2026-09-08** `1ee8fdf15424`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/dre/apis/dre-api/changes/cron/protocol-logs/post.md)

---

[API](https://skmtc.dev/dre/apis/dre-api.md) - [All operations](https://skmtc.dev/dre/apis/dre-api/llms.txt) - [OpenAPI document](https://skmtc.dev/dre/apis/dre-api/revisions/af4c974abc44?raw)
