> ## Documentation Index
> Fetch the complete documentation index at: https://surf-dcinside-api.kr.ask.surf/llms.txt
> Use this file to discover all available pages before exploring further.

# Calling from the DCinside Worker

> Transport helper, deadline configuration and recovery during rehearsal

The worker owns candidate delivery and would-be-pick logging. The latest rehearsal requirements are five-minute cycles and 60 seconds from sending, including at most one retry. Only the current cycle's on-time result may be used for that cycle's selection.

The repository includes `rtb_api.client.RTBClient`. It gzip-compresses cycle payloads, sends the key as the whole `Authorization` header and retries network errors plus `408`, `429`, `500`, `502`, `503` and `504`. It preserves the request ID and body.

The helper defaults to a 30-second per-operation timeout, at most one retry, and a shared 60-second total budget. It passes the remaining time to each attempt, shortens body-read socket timeouts after each chunk, checks after JSON decoding, and refuses a retry whose backoff cannot fit. An inline result received after the deadline raises `RTBDeadlineExceeded`; it is never returned as usable scores. GET recovery is a separate call with a separate budget.

The worker must still own a strict wall-clock decision cutoff. Synchronous `urllib` cannot safely preempt platform DNS resolution or every internal header/TLS operation; those phases can make an exception arrive after 60 seconds even though the helper refuses late results. This helper is not an OS-level cancellation guarantee. Treat all late completion as a missed decision cycle, and reconcile later without using recovered scores for selection.

```python theme={null}
from rtb_api.client import RTBClient

# Supply these from the integration configuration agreed by both teams.
client = RTBClient(
    base_url=rehearsal_base_url,
    api_key=secret_manager.get("RTB_API_KEY"),
    timeout_s=30,
    total_timeout_s=60,
    max_retries=1,
)

# New rehearsal candidates require a known queue-entry clock.
# The helper also validates RFC 3339 formatting and an explicit timezone.
for candidate in cycle_payload["candidates"]:
    if candidate.get("extracted_at") is None:
        raise ValueError("Rehearsal candidate has no queue-entry timestamp")

response = client.post_cycle(cycle_payload)
# The worker checks the deadline, cycle identity and freshness before
# logging would-be picks. It does not publish during rehearsal.
for row in response["results"]:
    score = row.get("score") if row["status"] == "scored" else None

# Optional dedup rankings never replace the complete score rows above.
dedup = response.get("deduplication")
if dedup:
    main = dedup["tiers"]["main"]
    # Inspect status/context/warnings; unchecked posts are not certified unique.
    checked_main_candidates = main["ranked"]

# When receipt is uncertain, recovery is separate from the decision deadline.
recovered = client.get_scores(cycle_payload["cycle_id"])
```

Treat this as a transport example, not a complete worker. It does not implement freshness checks, reconciliation, durable delivery or would-be-pick selection. The helper rejects unknown or invalid queue-entry clocks before sending new trial candidate data. Unknown clocks are data-quality failures to repair, not an automatic exclusion from the first-score SLA denominator. A recovered late score stays late for its original decision cycle.

Keep secrets out of payload files and logs. Keep unacknowledged data until receipt has been established. Read [Conventions](/conventions#retries-and-recovery) for the proposed replay protocol and durable restart and receipt semantics.

Supply optional `published_windows.main` and `.light` from the actual current published feeds. Each item can contain only `gall_id`, `post_no` and `placed_at`; archived candidate text and uploaded image bytes are reused after removal. Optional bootstrap content is needed only when that evidence was never received. Recommendations and `saved` removals do not advance those windows. Omitted context yields only candidate-pool deduplication, clearly marked partial; the client continues to enforce the actual rolling feed gate. See [Event deduplication](/deduplication).
