Skip to main content

Mock LLM Completion & Streaming Response API

MOCK DATA

Mock LLM completion fixtures with coherent token usage, tool calls and a replayable delta stream whose chunks concatenate to the exact message.

Generated test data for fixtures and development

Base URL
/api/llm-completions
Capabilities
2 routes Seedable
Last updated
July 30, 2026

GET /api/llm-completions

Live requestRuns against the public API
No key required
GET/api/llm-completions?count=10&seed=42

Request parameters

Pin the fictional model label and its latency profile. Omit for a per-record mix.

Pin the terminal reason and therefore the whole coherent shape. Omit for a weighted mix dominated by stop; tool_calls leaves the mix when maxTokens is below the cheapest tool fixture.

Completion budget in generated word-granular tokens. completionTokens never exceeds it, and finishReason=length always equals it exactly.

Echoed verbatim in every record and counted for promptTokens. Control characters are rejected. It does not influence the completion text or which tool is called. Omit for a per-record fixture prompt.

true forces at least one cached prompt token, false forces exactly zero. Omit for a cache hit on roughly a quarter of records.

Newest possible createdAt. Defaults to the end of the current UTC day and is echoed for replay; createdAt lands within seven days before it.

Advanced response options7 options

How many records to generate (1–100).

Deterministic output: the same seed always returns the same records. Omit for random (the used seed is echoed in meta.seed).

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Response format: json envelope, ndjson (one record per line) or csv.

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/llm-completions?seed=42&count=10"
const res = await fetch("https://randomapi.dev/api/llm-completions?seed=42&count=10");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/llm-completions?seed=42&count=10").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/llm-completions?seed=42&count=10"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 6
model enum

Pin the fictional model label and its latency profile. Omit for a per-record mix.

allowed: demo-model-sm | demo-model-md | demo-model-lg
example: model=demo-model-lg
finishReason enum

Pin the terminal reason and therefore the whole coherent shape. Omit for a weighted mix dominated by stop; tool_calls leaves the mix when maxTokens is below the cheapest tool fixture.

allowed: stop | length | tool_calls | content_filter
example: finishReason=length
maxTokens int

Completion budget in generated word-granular tokens. completionTokens never exceeds it, and finishReason=length always equals it exactly.

default: 64
allowed: 1 – 200
example: maxTokens=32
prompt string

Echoed verbatim in every record and counted for promptTokens. Control characters are rejected. It does not influence the completion text or which tool is called. Omit for a per-record fixture prompt.

allowed: 1 – 200
example: prompt=Summarize this changelog in three bullet points.
cached boolean

true forces at least one cached prompt token, false forces exactly zero. Omit for a cache hit on roughly a quarter of records.

example: cached=true
asOf date

Newest possible createdAt. Defaults to the end of the current UTC day and is echoed for replay; createdAt lands within seven days before it.

example: asOf=2026-07-30T23:59:59.999Z
Universal parameters Shared response and formatting options 7
count int

How many records to generate (1–100).

default: 10
allowed: 1 – 100
example: count=3
seed int

Deterministic output: the same seed always returns the same records. Omit for random (the used seed is echoed in meta.seed).

example: seed=42
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=id,model
exclude list

Return all fields except these (comma-separated).

example: exclude=latencyMs
format enum

Response format: json envelope, ndjson (one record per line) or csv.

default: json
allowed: json | ndjson | csv
example: format=csv
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 9
id string

Replay handle, shaped cmpl_<completionTokens base36>_<finish code><model code>_<8 lowercase hex>. In ids this route generates the last four hex digits are the record index, which is what keeps them unique within a response; /chunks treats any eight hex digits as a handle, so a hand-written id — like the example here — need not follow that convention. Pass it to /chunks to replay this exact stream.

example: cmpl_18_s2_5f3ac1d2

model string

Fictional model label; not a real or vendor model, and no model ran.

example: demo-model-md

createdAt string (ISO 8601)

Fictional creation instant, at or before the resolved asOf and within seven days of it.

example: 2026-07-28T11:42:07.318Z

prompt string

The supplied prompt, or a generated fixture prompt. It is not answered by message — no model is involved.

example: Summarize this changelog in three bullet points.

message object

{ role: "assistant", content }. content is generic English fixture prose, and an empty string for tool_calls and content_filter.

example: {"role":"assistant","content":"In practice, the copy is stored next to the source, reads go to the nearest replica. Each field has a default value, the record is versioned on update, the report covers the last seven days. Writes are ordered by a single owner, reads go."}

finishReason string

stop, length, tool_calls or content_filter, genuinely derived: length only when the budget was hit, tool_calls only when toolCalls is present.

example: stop

toolCalls object[] nullable

One or two { id, name, arguments } entries, where arguments is a JSON string that parses to an object. Non-null exactly when finishReason is tool_calls.

example: [{"id":"call_f780b593","name":"lookup_user","arguments":"{\"email\":\"dana@example.com\"}"}]

usage object

{ promptTokens, completionTokens, cachedPromptTokens, totalTokens }. Generated estimates, not tokenizer output: one token is one whitespace-delimited word, and a tool call costs 2 tokens plus one per 4 characters of arguments. cachedPromptTokens is a subset of promptTokens, never an addition, and totalTokens = promptTokens + completionTokens.

example: {"promptTokens":7,"completionTokens":44,"cachedPromptTokens":0,"totalTokens":51}

latencyMs integer

Fictional wall time for the whole completion: time to the first chunk plus every per-token step. Equals /chunks totalElapsedMs for the same id whenever that id is replayable.

example: 1194

Documented examples

Build-generated requests and complete responses
4
Reproducible mixed completions
GET /api/llm-completions?seed=42&count=10
{
  "data": [
    {
      "id": "cmpl_15_s3_51af0000",
      "model": "demo-model-lg",
      "createdAt": "2026-07-28T03:37:19.968Z",
      "prompt": "Write a commit message for this diff.",
      "message": {
        "role": "assistant",
        "content": "In practice, the flag is read once per session, the response is safe to cache, the job resumes from the last offset. The client backs off between retries, the copy is stored next to the source. Each request carries a trace."
      },
      "finishReason": "stop",
      "toolCalls": null,
      "usage": {
        "promptTokens": 7,
        "completionTokens": 41,
        "cachedPromptTokens": 1,
        "totalTokens": 48
      },
      "latencyMs": 1770
    },
    {
      "id": "cmpl_x_s1_86f30001",
      "model": "demo-model-sm",
      "createdAt": "2026-07-27T07:48:11.022Z",
      "prompt": "Propose three names for this feature flag.",
      "message": {
        "role": "assistant",
        "content": "In short, the check runs before every deploy, the report covers the last seven days. The summary is short by design, logs are sampled at a low rate, the summary is short by."
      },
      "finishReason": "stop",
      "toolCalls": null,
      "usage": {
        "promptTokens": 7,
        "completionTokens": 33,
        "cachedPromptTokens": 3,
        "totalTokens": 40
      },
      "latencyMs": 547
    },
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Truncated at the token budget
GET /api/llm-completions?finishReason=length&maxTokens=32&count=5
{
  "data": [
    {
      "id": "cmpl_w_l2_ad600000",
      "model": "demo-model-md",
      "createdAt": "2026-07-27T07:05:51.804Z",
      "prompt": "Describe what this route returns in one sentence.",
      "message": {
        "role": "assistant",
        "content": "Quick recap, the limit applies per client key, the job resumes from the last offset. Logs are sampled at a low rate, the build fails fast on a type error. The index"
      },
      "finishReason": "length",
      "toolCalls": null,
      "usage": {
        "promptTokens": 8,
        "completionTokens": 32,
        "cachedPromptTokens": 0,
        "totalTokens": 40
      },
      "latencyMs": 951
    },
    {
      "id": "cmpl_w_l1_017b0001",
      "model": "demo-model-sm",
      "createdAt": "2026-07-24T01:49:27.328Z",
      "prompt": "Suggest a shorter name for this variable.",
      "message": {
        "role": "assistant",
        "content": "The key point, the flag is read once per session, writes are ordered by a single owner, the worker pool scales with demand. Writes are ordered by a single owner, the header"
      },
      "finishReason": "length",
      "toolCalls": null,
      "usage": {
        "promptTokens": 7,
        "completionTokens": 32,
        "cachedPromptTokens": 0,
        "totalTokens": 39
      },
      "latencyMs": 622
    },
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Tool-call completions for one prompt
GET /api/llm-completions?finishReason=tool_calls&prompt=What+is+the+weather+in+Berlin%3F&count=5
{
  "data": [
    {
      "id": "cmpl_o_t2_a4cb0000",
      "model": "demo-model-md",
      "createdAt": "2026-07-25T09:28:28.330Z",
      "prompt": "What is the weather in Berlin?",
      "message": {
        "role": "assistant",
        "content": ""
      },
      "finishReason": "tool_calls",
      "toolCalls": [
        {
          "id": "call_09d5128b",
          "name": "create_ticket",
          "arguments": "{\"queue\":\"billing\",\"priority\":\"p2\"}"
        },
        {
          "id": "call_b39364cc",
          "name": "search_orders",
          "arguments": "{\"customerId\":\"cus_7be3c9\",\"status\":\"held\"}"
        }
      ],
      "usage": {
        "promptTokens": 6,
        "completionTokens": 24,
        "cachedPromptTokens": 0,
        "totalTokens": 30
      },
      "latencyMs": 907
    },
    {
      "id": "cmpl_b_t1_86650001",
      "model": "demo-model-sm",
      "createdAt": "2026-07-25T20:45:08.783Z",
      "prompt": "What is the weather in Berlin?",
      "message": {
        "role": "assistant",
        "content": """generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Cache-hit usage on the large model
GET /api/llm-completions?model=demo-model-lg&cached=true&count=20
{
  "data": [
    {
      "id": "cmpl_19_s3_991d0000",
      "model": "demo-model-lg",
      "createdAt": "2026-07-30T17:32:44.773Z",
      "prompt": "Rewrite this comment so it explains why.",
      "message": {
        "role": "assistant",
        "content": "To summarize, the response is safe to cache, the copy is stored next to the source, the id is stable across requests. The parser skips unknown keys, the worker pool scales with demand, the list is sorted by created time. The payload is trimmed to."
      },
      "finishReason": "stop",
      "toolCalls": null,
      "usage": {
        "promptTokens": 7,
        "completionTokens": 45,
        "cachedPromptTokens": 4,
        "totalTokens": 52
      },
      "latencyMs": 2323
    },
    {
      "id": "cmpl_8_s3_72710001",
      "model": "demo-model-lg",
      "createdAt": "2026-07-29T18:57:07.867Z",
      "prompt": "Write a commit message for this diff.",
      "message": {
        "role": "assistant",
        "content": "Here is the summary, state is kept out."
      },
      "finishReason": "stop",
      "toolCalls": null,
      "usage": {
        "promptTokens": 7,
        "completionTokens": 8,
        "cachedPromptTokens": 7,
        "totalTokens": 15
      },
      "latencyMs": 533
    },
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/llm-completions/chunks

Live requestRuns against the public API
No key required
GET/api/llm-completions/chunks?completionId=cmpl_18_s2_5f3ac1d2

Request parameters

An id from /api/llm-completions, used verbatim as the stream handle and decoded for its token count, finish reason and model — which come back as the completionTokens, finishReason and model fields. Mutually exclusive with tokens, finishReason and model.

Number of content tokens, and therefore of chunks. Rejected together with completionId, which already encodes it. Defaults to 24, and the resolved value is echoed in meta.params whenever no id was supplied.

Terminal reason: stop closes the text with a full stop, length stops mid-sentence with no closing punctuation. Rejected together with completionId, which already encodes it. Defaults to stop, and the resolved value is echoed in meta.params whenever no id was supplied.

Latency profile and record label. Rejected together with completionId, which already encodes it. Defaults to demo-model-md, and the resolved value is echoed in meta.params whenever no id was supplied.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/llm-completions/chunks?completionId=cmpl_18_s2_5f3ac1d2"
const res = await fetch("https://randomapi.dev/api/llm-completions/chunks?completionId=cmpl_18_s2_5f3ac1d2");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/llm-completions/chunks?completionId=cmpl_18_s2_5f3ac1d2").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/llm-completions/chunks?completionId=cmpl_18_s2_5f3ac1d2"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
completionId string

An id from /api/llm-completions, used verbatim as the stream handle and decoded for its token count, finish reason and model — which come back as the completionTokens, finishReason and model fields. Mutually exclusive with tokens, finishReason and model.

allowed: 5 – 32
example: completionId=cmpl_18_s2_5f3ac1d2
tokens int

Number of content tokens, and therefore of chunks. Rejected together with completionId, which already encodes it. Defaults to 24, and the resolved value is echoed in meta.params whenever no id was supplied.

allowed: 1 – 200
example: tokens=24
finishReason enum

Terminal reason: stop closes the text with a full stop, length stops mid-sentence with no closing punctuation. Rejected together with completionId, which already encodes it. Defaults to stop, and the resolved value is echoed in meta.params whenever no id was supplied.

allowed: stop | length
example: finishReason=length
model enum

Latency profile and record label. Rejected together with completionId, which already encodes it. Defaults to demo-model-md, and the resolved value is echoed in meta.params whenever no id was supplied.

allowed: demo-model-sm | demo-model-md | demo-model-lg
example: model=demo-model-lg
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=completionId,model
exclude list

Return all fields except these (comma-separated).

example: exclude=chunks
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 9
completionId string

The replay handle used: the supplied id, or the synthesized one.

example: cmpl_18_s2_5f3ac1d2

model string

Fictional model label decoded from the id, or the resolved one.

example: demo-model-md

finishReason string

stop or length; also carried on the final chunk.

example: stop

content string

The fully assembled message: exactly chunks.map(c => c.delta).join("").

example: In practice, the copy is stored next to the source, reads go to the nearest replica. Each field has a default value, the record is versioned on update, the report covers the last seven days. Writes are ordered by a single owner, reads go.

chunkCount integer

Number of chunks in this stream; equals completionTokens.

example: 44

completionTokens integer

Our word-granular token count for this completion — the same value the list route reports in usage.completionTokens for this id. Prompt-side counts are not encoded in an id, so they are deliberately absent here.

example: 44

timeToFirstChunkMs integer

chunks[0].elapsedMs; fictional latency, nothing was measured.

example: 366

totalElapsedMs integer

The last chunk's elapsedMs; equals the list route's latencyMs for this id.

example: 1194

chunks object[]

Ordered delta records: { index, delta, elapsedMs, finishReason }. delta is one word-granular fragment — the first has no leading space and every later one begins with exactly one space, where a real provider's deltas would be sub-word pieces. elapsedMs is cumulative and strictly increasing. finishReason is null on every chunk but the last.

example: [{"index":0,"delta":"In","elapsedMs":366,"finishReason":null}]

Documented examples

Build-generated requests and complete responses
3
Replay a completion's token stream
GET /api/llm-completions/chunks?completionId=cmpl_18_s2_5f3ac1d2
{
  "data": {
    "completionId": "cmpl_18_s2_5f3ac1d2",
    "model": "demo-model-md",
    "finishReason": "stop",
    "content": "In practice, the copy is stored next to the source, reads go to the nearest replica. Each field has a default value, the record is versioned on update, the report covers the last seven days. Writes are ordered by a single owner, reads go.",
    "chunkCount": 44,
    "completionTokens": 44,
    "timeToFirstChunkMs": 366,
    "totalElapsedMs": 1194,
    "chunks": [
      {
        "index": 0,
        "delta": "In",
        "elapsedMs": 366,
        "finishReason": null
      },
      {
        "index": 1,
        "delta": " practice,",
        "elapsedMs": 390,
        "finishReason": null
      },
      {
        "index": 2,
        "delta": " the",
        "elapsedMs": 417,
        "finishReason": null
      },
      {
        "index": 3,
        "delta": " copy",
        "elapsedMs": 440,
        "finishReason": null
      },
      {
        "index": 4,
        "delta": " is",
        "elapsedMs": 453,
        "finishReason": null"generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
A 24-chunk stream cut off at the token budget
GET /api/llm-completions/chunks?tokens=24&finishReason=length&model=demo-model-lg
{
  "data": {
    "completionId": "cmpl_o_l3_e10148e0",
    "model": "demo-model-lg",
    "finishReason": "length",
    "content": "At a high level, the path is resolved at build time, the cache holds warm keys for an hour. The index is rebuilt every",
    "chunkCount": 24,
    "completionTokens": 24,
    "timeToFirstChunkMs": 760,
    "totalElapsedMs": 2043,
    "chunks": [
      {
        "index": 0,
        "delta": "At",
        "elapsedMs": 760,
        "finishReason": null
      },
      {
        "index": 1,
        "delta": " a",
        "elapsedMs": 826,
        "finishReason": null
      },
      {
        "index": 2,
        "delta": " high",
        "elapsedMs": 888,
        "finishReason": null
      },
      {
        "index": 3,
        "delta": " level,",
        "elapsedMs": 942,
        "finishReason": null
      },
      {
        "index": 4,
        "delta": " the",
        "elapsedMs": 985,
        "finishReason": null"generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Default demo stream
GET /api/llm-completions/chunks
{
  "data": {
    "completionId": "cmpl_o_s2_f048c398",
    "model": "demo-model-md",
    "finishReason": "stop",
    "content": "At a high level, the summary is short by design, the report covers the last seven days, each field has a default value. The.",
    "chunkCount": 24,
    "completionTokens": 24,
    "timeToFirstChunkMs": 224,
    "totalElapsedMs": 832,
    "chunks": [
      {
        "index": 0,
        "delta": "At",
        "elapsedMs": 224,
        "finishReason": null
      },
      {
        "index": 1,
        "delta": " a",
        "elapsedMs": 256,
        "finishReason": null
      },
      {
        "index": 2,
        "delta": " high",
        "elapsedMs": 283,
        "finishReason": null
      },
      {
        "index": 3,
        "delta": " level,",
        "elapsedMs": 317,
        "finishReason": null
      },
      {
        "index": 4,
        "delta": " the",
        "elapsedMs": 343,
        "finishReason": null"generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

About this API

Coverage & behavior

Generates mock LLM completion records plus a replayable token stream, for tests, streaming interfaces and usage panels. There is no model behind this endpoint: prompt is echoed and counted, and message.content is generic English fixture prose assembled from a seeded word bank. It never answers the prompt, and the tool a tool_calls record picks is unrelated to it.

Token counts are generated estimates, not tokenizer output. One token is one whitespace-delimited word; a tool call costs 2 tokens for its name plus one per 4 characters of arguments. A real sub-word tokenizer splits words into pieces and would report more, so never use these numbers for billing, pricing or context-window math. cachedPromptTokens is a subset of promptTokens, never an addition: totalTokens = promptTokens + completionTokens.

Every record is internally coherent. finishReason=length returns exactly maxTokens completion tokens and text that stops mid-sentence with no closing punctuation; stop returns text that closes its sentence with a full stop (the word bank carries no punctuation of its own, so . is the only terminal character this generator emits) and stays under the budget wherever the budget allows it (only maxTokens=1 forces the two to coincide, so length implies the budget was hit but not the reverse); content_filter returns an empty message and zero completion tokens; tool_calls returns an empty message plus one or two calls whose arguments is a JSON string that parses to an object. toolCalls is non-null exactly when finishReason is tool_calls, and a budget too small for the cheapest tool fixture drops tool_calls from the mix — asking for it explicitly is a 400.

/chunks replays one completion as its ordered delta records. chunks.map(c => c.delta).join("") equals content byte for byte, chunkCount equals completionTokens, and totalElapsedMs equals that record's latencyMs. Any completion's id is the replay handle: it encodes the token count, finish reason and model, and the whole stream is derived from the id string alone, so the same id always rebuilds the same message. To vary a stream by hand, change the eight hex digits at the end of an id.

Called without an id, /chunks synthesizes one from tokens, finishReason and model and returns it in completionId, so you can paste that handle into a later replay. Because an id and those three params are mutually exclusive, meta.params echoes only whichever set was in force — the id on its own, or the resolved trio on its own — and is therefore always a query you can re-issue verbatim.

/chunks is a computed route rather than a generated list, deliberately. A list route would advertise a seed it could not honor, because every chunk record would get its own derived seed and the chunks could not then agree on one message. Instead the whole bounded sequence arrives in one JSON document, and the route reads no clock.

Deliberate limits, so nothing here over-promises. There is no server-sent-events or HTTP streaming: no text/event-stream, no data: frames, no end sentinel. There is no POST route, no request body, no system prompt and no multi-turn input — chat threads live at /api/conversations. The field names are ours; this is not a drop-in for any provider's wire format or SDK, and there are no sampling parameters, no log probabilities, no reasoning tokens and no price fields. There is also no 404: an id is a computed replay handle rather than a key in a finite registry, so a bad id is a 400 rather than a miss — one that quotes the value when the shape is wrong, and names the decoded reason (a tool-call, filtered, empty or over-long completion) when the shape is fine but there is no text to stream.

One honest consequence of a tiny budget: at maxTokens=1 with finishReason=stop every message is a single word, so messages repeat across records even though the records themselves stay distinct through their ids.

Use it for

  • Drive a streaming chat interface from a replayable token stream with no model call
  • Build fixtures for usage panels that show prompt, cached and completion token counts
  • Exercise finish-reason handling for truncated, filtered and tool-call responses
  • Reproduce one exact completion in CI by pasting its replay id into a test

Frequently asked questions

How do I mock an LLM streaming response in a test?

Call /api/llm-completions/chunks: it returns the ordered delta records as one JSON document. Concatenate every delta in order to get the message, or feed the records to your own emitter to simulate arrival over time.

Do the deltas reconstruct the completion exactly?

Yes, byte for byte. chunks.map(c => c.delta).join("") equals content, and chunkCount equals completionTokens, because both routes build the stream from the same id-seeded function.

Are the token counts real tokenizer counts?

No. One token is one whitespace-delimited word, and a tool call costs 2 tokens plus one per 4 characters of its arguments. A real sub-word tokenizer would report more, so never use these numbers for billing, pricing or context-window estimates.

Can I point an SDK at this endpoint?

No. It is GET-only JSON with our own field names: there is no POST completion route, no text/event-stream, and no wire-format conformance claim for any provider. Use it as fixture data inside your tests.

Does the completion text answer my prompt?

No. There is no model here. prompt is echoed and counted, and the completion text is generic English fixture prose generated from a seeded word bank.

How do I replay one completion's stream?

Pass that record's id to /chunks. The id is a replay handle that encodes the token count, finish reason and model, so the deltas rebuild that exact message.