Reasoning Continuation — ML Junction docs

Reasoning continuation lets a later request reuse provider-native reasoning state and prefer the exact route that produced it. It is optional. You can always send ordinary complete message history without a continuation token; the model will continue from the visible conversation through normal routing.

The simple mental model

A continued conversation has three separate pieces. Your application owns the visible messages and provider reasoning details. ML Junction owns only a small encrypted routing ticket. The message history explains what the conversation means; reasoning details carry provider-native state; the routing ticket identifies where that state can be replayed safely.

Piece · Owner · What it contains · What to do with it
messages · Your application · Complete visible system, user, assistant, and tool history · Store in order and send the complete history on the next turn
messages[].reasoning_details · Your application · Provider-native reasoning items; some parts may be readable and some opaque · Keep each array on the assistant message that produced it and round-trip it unchanged
reasoning.continuation_token · ML Junction · Encrypted issue time and routing affinity for the provider, provider model, route, eligible provider account, API family, and compatibility key · Treat as opaque; store per conversation branch and send the newest successful token

The continuation token never contains an API key, provider credential, key ID, organization or tenant ID, billing account, conversation history, prompt, answer, reasoning text, signature, or provider-encrypted reasoning content. Provider API keys remain server-configured and are never returned to your application.

Continuation is optional

If you omit reasoning.continuation_token, the request is a normal stateless conversation turn. Complete visible history still gives the selected model the conversation context. What you give up is exact route/account affinity, provider-native reasoning replay, and any cache benefit that depends on returning to the same provider surface.

What you send · Allowed? · Result
Complete history only · Yes · Normal routing and fallback; the model continues from visible messages
Complete history + token + matching reasoning_details · Yes; recommended for native continuation · Exact origin is preferred first; native state can be replayed and provider cache affinity may improve
Complete history + token, but no reasoning_details · Yes · Route affinity may still be attempted; native state is not reconstructed and PRIOR_REASONING_WILL_NOT_BE_RENDERED is returned
Complete history + reasoning_details, but no token · Yes · Visible history continues normally; provider-native details are not trusted for replay and PRIOR_REASONING_NOT_REUSABLE is returned
Only a token, without the earlier conversation · Not sufficient for conversation context · The token has no hidden message history; send complete history if the new turn depends on it

1. Start a reasoning turn

curl "$ASTRO_BASE_URL/v1/responses" \
  -H "Authorization: Bearer $ASTRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "session_id": "sess_checkout_123",
    "messages": [
      {
        "role": "user",
        "content": "A customer paid $120 after a 20% discount. What was the original price?"
      }
    ],
    "reasoning": {
      "enabled": true,
      "effort": "medium",
      "summary": "concise"
    },
    "requirements": {
      "reasoning": "required"
    },
    "output": {
      "max_tokens": 1200
    }
  }'

2. Save the response state

{
  "id": "resp_01J...",
  "session_id": "sess_checkout_123",
  "model": "gpt-5.5",
  "output": [
    {
      "type": "text",
      "text": "The original price was $150."
    }
  ],
  "reasoning": {
    "summary": "I treated $120 as 80% of the original price.",
    "details": [
      {
        "type": "reasoning",
        "summary": [
          {
            "type": "summary_text",
            "text": "I divided the paid amount by 0.8."
          }
        ],
        "encrypted_content": "provider-opaque-state"
      }
    ],
    "continuation_token": "gwrt_v3.opaque-routing-ticket",
    "phase": "completed",
    "reusable": true,
    "usage": {
      "reasoning_tokens": 84
    }
  },
  "usage": {
    "input_tokens": 31,
    "output_tokens": 112,
    "reasoning_tokens": 84,
    "total_tokens": 143,
    "provider_reported": true,
    "confidence": "actual"
  },
  "warnings": [],
  "routing": {
    "selected_route": "openai-gpt-5.5-global-standard",
    "provider": "openai",
    "provider_model": "gpt-5.5",
    "fallback_used": false,
    "attempts": 1
  },
  "receipt": {
    "route_id": "openai-gpt-5.5-global-standard",
    "provider": "openai",
    "provider_model": "gpt-5.5",
    "actual_charge_usd": 0.0012,
    "cache": {
      "status": "miss",
      "cached_input_tokens": 0,
      "reason": "provider_reported"
    }
  }
}

Append the visible assistant answer to your conversation history.. Copy reasoning.details to reasoning_details on that exact assistant message.. Store reasoning.continuation_token as the latest token for this conversation branch.. Store reasoning.phase so your application knows whether tool results are pending.. Replace stored continuation state only after a successful terminal response.

Readable summaries versus opaque transport state

Field · May be shown to an end user? · Handling
reasoning.summary · Yes, when your product policy permits · Render as an optional provider-generated summary, not guaranteed chain of thought
Readable summary/thinking text inside reasoning.details · Yes, when clearly identified and permitted · Render only known readable text fields
usage.reasoning_tokens · Yes · Show as numeric usage telemetry; it is not readable reasoning and is already part of output usage
reasoning.continuation_token · No · Opaque transport value; never render, decode, log, place in a URL, or send to analytics
encrypted_content · No · Preserve unchanged for replay; never display as reasoning text
signature or provider_signature · No · Preserve unchanged; never expose as user-readable content
provider-account reference · No · Internal encrypted routing metadata; it is not a credential and is never exposed as a public field

3. Continue with complete history

{
  "model": "gpt-5.5",
  "session_id": "sess_checkout_123",
  "messages": [
    {
      "role": "user",
      "content": "A customer paid $120 after a 20% discount. What was the original price?"
    },
    {
      "role": "assistant",
      "content": "The original price was $150.",
      "reasoning_details": [
        {
          "type": "reasoning",
          "summary": [
            {
              "type": "summary_text",
              "text": "I divided the paid amount by 0.8."
            }
          ],
          "encrypted_content": "provider-opaque-state"
        }
      ]
    },
    {
      "role": "user",
      "content": "What if the discount was 25% instead?"
    }
  ],
  "reasoning": {
    "enabled": true,
    "effort": "medium",
    "summary": "concise",
    "continuation_token": "gwrt_v3.opaque-routing-ticket",
    "input_type": "user_message"
  },
  "requirements": {
    "reasoning": "required"
  },
  "output": {
    "max_tokens": 1200
  }
}

Complete history and the token do not override each other. History controls the meaning of the conversation. The token controls safe routing affinity. reasoning_details supplies the provider-native state. If a user edits, removes, or reorders their own history/state, the provider may reject native continuation; ML Junction does not secretly reconstruct client-owned data.

Soft expiration after 24 hours by default

gwrt_v3 contains an encrypted issue time. The default continuation lifetime is 24 hours, although a deployment may configure a different value. There is no public expires_at field and clients should not decode or calculate token age. Simply send the newest token; the gateway handles expiration.

{
  "output": [
    {
      "type": "text",
      "text": "Continuing from the visible conversation..."
    }
  ],
  "warnings": [
    {
      "code": "REASONING_CONTINUATION_EXPIRED",
      "field": "reasoning.continuation_token",
      "action": "ignored",
      "reason": "routing_affinity_token_expired",
      "requested": null,
      "effective": null
    }
  ],
  "reasoning": {
    "continuation_token": "gwrt_v3.fresh-routing-ticket",
    "phase": "completed",
    "reusable": true
  }
}

Expiration is not an error and does not delete visible conversation history.. The expired routing affinity and provider-native details are not replayed.. The request goes through normal capability, health, privacy, cost, retry, and fallback routing.. A successful response supplies fresh continuation state for the route that actually completed the request.. If you sent only a token and omitted required history, the gateway cannot recover conversation context because tokens never contain messages.

Provider, route, or account removed between turns

ML Junction prefers the exact origin route and eligible provider account first. It does not make that preference a single point of failure. If the route was disabled or deleted, the provider account was removed, the route is in health cooldown, or its credential is no longer eligible, ML Junction abandons only native replay and continues through the normal routing plan using visible history.

{
  "warnings": [
    {
      "code": "PRIOR_REASONING_NOT_REUSABLE",
      "field": "reasoning.continuation_token",
      "action": "ignored",
      "reason": "origin_route_provider_or_account_unavailable"
    }
  ],
  "routing": {
    "selected_route": "vertex-gemini-fallback",
    "provider": "vertex_ai",
    "provider_model": "gemini-3.5-pro",
    "fallback_used": false,
    "attempts": 1,
    "decision_reason": "primary_route"
  },
  "reasoning": {
    "continuation_token": "gwrt_v3.new-origin-ticket"
  }
}

Graceful routing still respects your request. An explicitly pinned provider/route, provider allow-list, required capability, ZDR rule, context requirement, charge cap, or disabled fallback can legitimately leave no eligible alternative. "Graceful fallback" never means bypassing your privacy, capability, billing, or authorization constraints.

Origin provider fails during the continued request

The origin candidate receives provider-native state. If it fails and another eligible candidate is attempted, ML Junction removes native replay before calling that candidate and sends only portable visible history. The response warning reason is origin_route_failed_falling_back_to_visible_history. This prevents OpenAI-, Anthropic-, Gemini-, DeepSeek-, or xAI-specific state from crossing into an incompatible provider.

Same-route retry behavior follows routing.fallbacks.max_retries_per_route.. Cross-route and cross-provider behavior follows routing.fallbacks and max_total_attempts.. The fallback candidate must satisfy the same model capability, privacy, context, account-policy, and cost rules.. Continuation-bearing streams are buffered so the gateway can validate terminal provider state and recover before exposing a partial answer.. The successful fallback becomes the new continuation origin and returns a new gwrt_v3 token.

Tampered tokens are different from expired tokens

Situation · HTTP result · What your application should do
Expired authentic token · Request can succeed with a warning · Keep visible history, accept the fresh token from the successful response
Removed/unavailable origin · Request can succeed through normal routing with a warning · Use the selected route evidence and replace continuation state on success
Provider outage before completion · Normal retries/fallbacks can succeed · Do not issue a duplicate application retry when the gateway already succeeded
Modified, malformed, wrong-version, or non-canonical token · HTTP 400 INVALID_REASONING_CONTINUATION · Discard that token and start a fresh history-only turn; never retry the same token
No eligible fallback exists · Normal capability/configuration/provider error · Relax only the constraint your product is allowed to relax, or retry later when retryable
{
  "error": {
    "type": "invalid_request_error",
    "message": "The reasoning continuation routing token is invalid or has been modified.",
    "code": "INVALID_REASONING_CONTINUATION",
    "request_id": "req_01J...",
    "support_code": "support_01J...",
    "retryable": false,
    "details": {}
  }
}

Conversation edits, regeneration, and branching

Normal next turn: send the complete branch history, reasoning details on their original assistant turns, and the latest token.. Edit an earlier user message: create a new branch and normally clear continuation state because the old provider state represents a different prefix.. Regenerate an assistant turn: treat the regenerated response and its returned token/details as the new branch head.. Fork: copy the visible history you want into a new branch and use pending_turn_action=fork when resolving a pending tool loop; native state may be abandoned with a warning.. Cancel a pending tool loop: keep the visible transcript, send pending_turn_action=cancel, and expect native continuation to be abandoned.. Never share one conversation token across unrelated users, sessions, tabs, or branches.

JavaScript state and request construction

const conversation = {
  sessionId: 'sess_checkout_123',
  messages: [],
  continuation: {
    token: null,
    phase: 'completed',
    reusable: false,
  },
};
function textFromOutput(output = []) {
  return output
    .filter((item) => item.type === 'text')
    .map((item) => item.text ?? '')
    .join('');
}
function acceptSuccessfulResponse(state, response) {
  state.messages.push({
    role: 'assistant',
    content: textFromOutput(response.output),
    tool_calls: response.output
      .filter((item) => item.type === 'tool_call')
      .map((item) => item.tool_call),
    reasoning_details: response.reasoning?.details ?? [],
  });
  state.continuation = {
    token: response.reasoning?.continuation_token ?? null,
    phase: response.reasoning?.phase ?? 'completed',
    reusable: response.reasoning?.reusable ?? false,
  };
}
function buildNextTurn(state, userText) {
  const messages = [
    ...state.messages,
    { role: 'user', content: userText },
  ];
  return {
    model: 'gpt-5.5',
    session_id: state.sessionId,
    messages,
    reasoning: {
      enabled: true,
      effort: 'medium',
      summary: 'concise',
      ...(state.continuation.token
        ? { continuation_token: state.continuation.token }
        : {}),
      input_type: 'user_message',
    },
  };
}

Persist continuation state only in storage appropriate for your threat model. Do not put it in a query string, page title, crash report, analytics payload, support screenshot, or client-visible debug panel. It is not an API key, but it is confidential routing metadata and should be handled as an opaque session value.

OpenAI and Anthropic compatibility surfaces

Surface · Send continuation controls · Receive/preserve provider state
Native /v1/responses · reasoning.continuation_token, input_type, pending_turn_action · reasoning.details; attach it to the assistant as reasoning_details
OpenAI /openai/v1/chat/completions · mlj.reasoning inside the compatibility extension · Assistant reasoning_details plus mlj.reasoning metadata
Anthropic /anthropic/v1/messages · mlj.reasoning inside the compatibility extension · Native thinking/redacted_thinking blocks plus mlj.reasoning metadata

Use the native Responses API when continuation, route receipts, structured warnings, and portable fallback behavior are first-class application features. Compatibility surfaces preserve their vendor protocol shape and carry ML Junction additions under mlj.

Canonical URL: https://mljunction.com/docs/reasoning-continuation