When you enable both citations and streaming ("stream": true), Claude's answer and its supporting citations interleave in the same server-sent event stream. Text arrives as text_delta events; citations arrive as citations_delta events. A UI that understands both can render source attributions progressively — footnote markers appearing next to sentences moments after the sentences themselves — instead of bolting all sources on at the end.
Where citations_delta fits in the event flow
The streaming protocol frames every response the same way: a message_start, then for each content block a content_block_start, one or more content_block_delta events, and a content_block_stop; then message_delta and a final message_stop. Each content block carries an index matching its position in the final content array. The content_block_delta event is a container for several delta types — text_delta, input_json_delta, thinking_delta — and, when citations are enabled, citations_delta.
The contract for citations is precise: each citations_delta carries exactly one citation, appended to the citations list of the current text block. You never receive a batch of citations in one delta, and the citation always belongs to the text block currently streaming at that index. A typical fragment looks like:
event: content_block_delta
data: {"type": "content_block_delta", "index": 0,
"delta": {"type": "citations_delta",
"citation": {"type": "char_location",
"cited_text": "The refund window is 30 days.",
"document_index": 0,
"start_char_index": 812,
"end_char_index": 841}}}
The citation's shape depends on the document type you attached: char_location for plain text (0-indexed character range), page_location for PDFs (1-indexed page range), content_block_location for custom content documents, and search_result_location for search-result blocks. document_index is 0-indexed across all document blocks in the request.
Building the progressive-attribution UI
The one-citation-per-delta rule makes client state simple. Per text block index, keep a text buffer and a citations array, and fold events in:
content_block_start— allocate state for that block index.text_delta— append to the block's text buffer and render.citations_delta— push the citation onto the block's array and render a source marker at the current end of the text. Because the delta arrives while its supporting sentence is the freshest text on screen, appending a numbered marker "where the cursor is" places attribution correctly without any offset math.content_block_stop— finalize the block.
For the source panel itself, cited_text gives you the verbatim supporting passage for a hover preview — and it does not count toward output tokens, so rich previews cost nothing extra. Map document_index to the document's title you supplied at request time for a human-readable label. Two practical notes: expect ping events anywhere in the stream, and handle in-stream error events (such as overloaded_error) by failing visibly rather than leaving a half-rendered answer looking complete.
Platform notes
Streaming and citations are both broadly available for Claude across the third-party platforms — the Claude API, Claude Platform on AWS, Amazon Bedrock, and Google Vertex AI, with Microsoft's Foundry docs listing citations among supported capabilities — so this event-handling pattern travels with you. The main portability caveat sits a level up: if you built attribution on a cloud-native RAG service instead (for example Bedrock Knowledge Bases' citations[] response arrays), the wire format is that service's, not the citations_delta stream described here. Anthropic-native citations keep one client implementation working on every platform where you call the Messages API.
Where to go next
For the full event vocabulary, see streaming event types; for choosing document types and citation granularity, see custom content documents; for what citations cost, see citations billing mechanics.