Skip to content

Document the response differences between Horizon and RPC - #2828

Open
kaankacar wants to merge 6 commits into
mainfrom
docs-agent/1621-horizon-rpc-response-differences
Open

Document the response differences between Horizon and RPC#2828
kaankacar wants to merge 6 commits into
mainfrom
docs-agent/1621-horizon-rpc-response-differences

Conversation

@kaankacar

Copy link
Copy Markdown
Contributor

🤖 Automated triage bot, acting for @kaankacar.

Refs #1621. The migration guide maps each Horizon endpoint to an RPC method, but it never said the two responses have different shapes. This adds a Response Differences section that compares GET /accounts/{address} with getLedgerEntries side by side, using real Testnet responses, and covers response shape, raw stroop amounts, XDR encoding, the flags and thresholds fields, and the missing-entry case. It also links the migration guide from the APIs overview, which had no inbound link to it.

This uses "Refs", not "Closes". The issue asks for a comparison of the most-used Horizon endpoints, and this delivers the account endpoint only. Please close the issue when the remaining endpoints are covered.

Verified against live Testnet data and primary sources:

  • Horizon 9009.2998203 XLM equals the account entry's 90092998203 stroops. Horizon 1252.7872975 USDC equals the trustline entry's 12527872975.
  • AccountEntry in stellar-xdr v28.0 has no trustline field and comments int64 balance as "in stroops".
  • The XDR comment on thresholds reads [weight of master|low|medium|high], which matches the live "01000000" next to Horizon's master key signer of weight 1.
  • TrustLineFlags sets AUTHORIZED_FLAG = 1, which matches the live flags: 1 and Horizon's is_authorized.
  • The openrpc spec sets the 200-key limit and warns that the xdrFormat: "json" shape is not stable.
  • A key for an account that does not exist returns HTTP 200 with that key absent from entries. Horizon returns 404 for the same account.

The migration guide maps each Horizon endpoint to an RPC method, but it
never said that the two responses have different shapes. That gap is the
one people hit first: Horizon aggregates an account with its trustlines
and formats amounts to seven decimal places, while getLedgerEntries
returns one raw ledger entry per key with amounts as int64 stroops.

Add a Response Differences section that compares GET /accounts/{address}
with getLedgerEntries side by side, using real Testnet responses. It
covers response shape, raw amounts, XDR encoding, the flags and
thresholds fields, and the fact that a missing entry is a 200 rather
than a 404.

Also link the migration guide from the APIs overview, which had no
inbound link to it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds documentation to help developers migrating from Horizon to RPC understand and handle the different response shapes—specifically by introducing a side-by-side “Response Differences” section in the migration guide and adding an inbound link from the APIs overview.

Changes:

  • Add an inbound link from the APIs overview to the Horizon→RPC migration guide.
  • Add a “Response Differences” section comparing Horizon GET /accounts/{address} to RPC getLedgerEntries, including worked example responses and guidance on amounts, encoding, flags/thresholds, and missing-entry behavior.
  • Update the endpoint mapping row for GET /accounts/{address} to point readers to the new example section.

Recommendation: NEEDS-CHANGES — the RPC example response should include entry keys (to support the “missing entry” guidance), and the new XDR reference should be consistent with the rest of the RPC docs.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
docs/data/apis/README.mdx Adds a direct link to the migration guide from the APIs overview.
docs/data/apis/migrate-from-horizon-to-rpc.mdx Adds a new “Response Differences” section and links it from the endpoint mapping table.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +101 to +124
"entries": [
{
"dataJson": {
"account": {
"balance": "90092998203",
"num_sub_entries": 1,
"flags": 0,
"thresholds": "01000000",
"signers": []
}
},
"lastModifiedLedgerSeq": 1397061
},
{
"dataJson": {
"trustline": {
"balance": "12527872975",
"limit": "9223372036854775807",
"flags": 1
}
},
"lastModifiedLedgerSeq": 3657583
}
],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated message from Kaan's Automated Triage Bot.

Correct, and fixed. One detail: with "xdrFormat": "json" the field is keyJson, not key. A live Testnet call returns keyJson and dataJson per entry.

Changes:

  • The example now shows keyJson on both entries.
  • The missing-entry section names the field to match on (keyJson, or key in base64 format).

:::

[CAP-67]: https://github.com/stellar/stellar-protocol/blob/master/core/cap-0067.md
[`AccountEntry`]: https://github.com/stellar/stellar-xdr/blob/v28.0/Stellar-ledger-entries.x#L190

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated message from Kaan's Automated Triage Bot.

Keeping v28.0. Reasons:

  • v28.0 is the latest stellar-xdr tag.
  • AccountEntry is at line 190 in both v25.0 and v28.0, so the link is correct.
  • The docs pin no single tag. Other pages use next, curr, v22.0, v25.0, v27.0 and raw commits.

The v25.0 links on the getLedgerEntries page are older. Re-tagging that page is a separate change.

@github-actions github-actions Bot added the preview Preview builds for PRs by SDF employees. label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Copilot AI review requested due to automatic review settings September 8, 2026 03:07
@github-actions github-actions Bot removed the preview Preview builds for PRs by SDF employees. label Sep 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

docs/data/apis/migrate-from-horizon-to-rpc.mdx:24

  • The table says RPC returns a “Signed 64-bit integer”, but in JSON responses these int64 values are typically serialized as strings (as shown in the example snippet). Clarifying this avoids suggesting callers can safely treat the value as a JSON number.

This issue also appears on line 162 of the same file.
docs/data/apis/migrate-from-horizon-to-rpc.mdx:195

  • This row uses “trust line” but the docs elsewhere (including this page’s earlier “trustline entries”) consistently use “trustline” as a single term. Aligning terminology will reduce confusion for readers searching the docs.

docs/data/apis/migrate-from-horizon-to-rpc.mdx:162

  • “RPC returns the integer itself” is a bit misleading in the xdrFormat: "json" context, since int64 amounts are usually returned as JSON strings to preserve precision. Tweaking the wording here will align with the example response and the big-number advice.
Every classic amount on the ledger is a signed 64-bit integer of stroops. Horizon divides it by ten million and returns a string with seven decimal places. RPC returns the integer itself. Divide it in your own code, and use a big-number type so you do not lose precision. See [Amount precision](../../learn/fundamentals/stellar-data-structures/assets.mdx#amount-precision).

@github-actions github-actions Bot added the preview Preview builds for PRs by SDF employees. label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Copilot AI review requested due to automatic review settings September 8, 2026 03:13
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Round 2 had no active comments. Three suppressed comments, all accepted and fixed in 5e0dd14.

  • int64 amounts: correct. A live Testnet call returns "balance": "90092998203", a JSON string. The table now says "sent as a string". The Amounts section now says the value is a JSON string, not a JSON number.
  • "trust line" in the mapping table: correct. 88 docs files use "trustline", 11 use "trust line". Changed to "trustline".

@github-actions github-actions Bot removed the preview Preview builds for PRs by SDF employees. label Sep 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

docs/data/apis/migrate-from-horizon-to-rpc.mdx:166

  • This section describes missing keys as being "left out of entries", but per the RPC OpenRPC schema entries itself may be omitted when no entries are found. Mentioning this avoids clients assuming entries is always an array.
### A Missing Entry Is Not an Error

Horizon answers `404` when a resource does not exist. RPC answers `200` and leaves the key out of `entries`, so a request for two keys can return one entry. Match each returned entry back to its own key with the entry's `keyJson` field, or `key` in the default base64 format. Do not match on the position you sent it in.

Comment on lines +25 to +28
| Encoding | Parsed JSON fields | Base64 XDR, or JSON when you pass `xdrFormat` |
| Flags and thresholds | Named booleans and named thresholds | A raw `uint32` bitmask and a four-byte hex string |
| Entry not found | HTTP `404` | HTTP `200`, and the key is absent from `entries` |
| Collections | HAL `_links` and a `paging_token` for paging | No paging, and a limit of 200 keys per call |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated message from Kaan's Automated Triage Bot.

Partly correct. Fixed in 964c565.

  • Accepted: the missing-key case needed the all-missing shape. The table and the section now say entries is empty when no requested key exists.
  • Rejected: "omitted entirely". A live Testnet call with one nonexistent account key returns {"entries":[],"latestLedger":4562786}. The array is present and empty.
  • The schema point is real, so the section now says the specification requires only latestLedger and tells the reader to read entries defensively.

@github-actions github-actions Bot added the preview Preview builds for PRs by SDF employees. label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Copilot AI review requested due to automatic review settings September 8, 2026 03:19
@github-actions github-actions Bot removed the preview Preview builds for PRs by SDF employees. label Sep 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

}
```

RPC answers with one entry per key. Each entry repeats the key it answers, in the `keyJson` field. This is the `result` object of that response, trimmed the same way:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated message from Kaan's Automated Triage Bot.

Correct, and fixed in 2da35bb. The same wording was in the comparison table, so both changed.

  • "one entry per key" is now "one entry for each key it finds".
  • The Shape row is now "One ledger entry for each requested key that exists".

@github-actions github-actions Bot added the preview Preview builds for PRs by SDF employees. label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Copilot AI review requested due to automatic review settings September 8, 2026 03:23
@github-actions github-actions Bot removed the preview Preview builds for PRs by SDF employees. label Sep 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines +24 to +26
| Amounts | Decimal string with seven places, such as `"1252.7872975"` | Signed 64-bit integer of stroops, sent as a string, such as `"12527872975"` |
| Encoding | Parsed JSON fields | Base64 XDR, or JSON when you pass `xdrFormat` |
| Flags and thresholds | Named booleans and named thresholds | A raw `uint32` bitmask and a four-byte hex string |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated message from Kaan's Automated Triage Bot.

Correct, and fixed in 3103cda. One line under the table now says the RPC column describes the decoded entry, and that a default response keeps those fields inside the base64 XDR. This covers the Amounts row and the Flags and thresholds row together.

This is the last fix round on this PR. Later Copilot rounds get an adjudication reply, not a push.

Copilot AI review requested due to automatic review settings September 8, 2026 03:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (2)

docs/data/apis/migrate-from-horizon-to-rpc.mdx:27

  • The table implies entries will always be present and empty when nothing is found, but the OpenRPC schema only requires latestLedger in the result, so clients should also tolerate entries being omitted. Consider softening this row to avoid guaranteeing entries is present.
| Entry not found | HTTP `404` | HTTP `200`, and the key is absent from `entries`, which is empty when no key exists |

docs/data/apis/migrate-from-horizon-to-rpc.mdx:168

  • This paragraph states entries is empty when no key exists, but per the OpenRPC schema entries is optional (only latestLedger is required). Updating the wording to “empty or omitted” keeps the doc aligned with the spec and matches the recommendation to read entries defensively.
Horizon answers `404` when a resource does not exist. RPC answers `200` and leaves the key out of `entries`, so a request for two keys can return one entry. If no key exists, `entries` is empty. The specification requires only `latestLedger` in the result, so read `entries` defensively.

@github-actions github-actions Bot added the preview Preview builds for PRs by SDF employees. label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Verified: every value in both examples matches a live Testnet call, the XDR and OpenRPC claims match their sources, and the preview renders with all links resolving. The body says "Refs #1621", which is right, because only the account endpoint is covered. Ready to merge.

@kaankacar kaankacar added the bot:ready-to-merge Bot verified and approved; waiting for a maintainer to merge label Sep 8, 2026

@ElliotFriend ElliotFriend left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the content here looks fine to me. my main question is if this is the right place on the page for this section? the table seems like the most "useful" content on the page, and this is maybe a good fit to follow that information? we could (and should) link to it from the (early on) "request/response format" section, but i'm not sure about displacing the table so heavily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:ready-to-merge Bot verified and approved; waiting for a maintainer to merge preview Preview builds for PRs by SDF employees.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants