Before You Debug
Most integration issues come from one of four causes:- Mixing devnet and mainnet values.
- Signing the wrong activation message.
- Using an expired or missing guest JWT.
- Deriving validation PDAs from a timestamp that does not match the proof payload.
| Network | Program ID | Guest JWT URL | Activation URL | API Base |
|---|---|---|---|---|
| Mainnet | 9ExbZjAapQww1vfcisDmrngPinHTEfpjYRWMunJgcKaA | https://txline.txodds.com/auth/guest/start | https://txline.txodds.com/api/token/activate | https://txline.txodds.com/api/ |
| Devnet | 6pW64gN1s2uqjHkn1unFeEjAwJkPGHoppGvS715wyP2J | https://txline-dev.txodds.com/auth/guest/start | https://txline-dev.txodds.com/api/token/activate | https://txline-dev.txodds.com/api/ |
Activation Checklist
Before calling/api/token/activate, confirm:
- The on-chain
subscribe(serviceLevelId, durationWeeks)transaction is confirmed. - The
txSigis from the same network as the activation endpoint. - The guest JWT came from the same network host.
- The signing wallet is the same wallet that submitted the
subscribetransaction. walletSignatureis a base64-encoded detached signature.leaguesis[]for the standard free World Cup and International Friendlies bundle.
selectedLeagues = [], sign:
Runnable Example Checklist
When running the repository examples inexamples/devnet, confirm:
- You are using Node.js
20or newer. The current lockfile resolveseventsource@4.1.0, which requiresnode >=20.0.0. ANCHOR_PROVIDER_URLpoints tohttps://api.devnet.solana.com.TOKEN_MINT_ADDRESSis the devnet TxL mint4Zao8ocPhmMgq7PdsYWyxvqySMGx7xb9cMftPMkEokRG.- The script imports the devnet IDL/types from
examples/devnet/idlandexamples/devnet/types. - The wallet in
ANCHOR_WALLEThas enough devnet SOL for subscription transactions and account rent. - Fixed
fixtureIdandseqpairs in example scripts are demo fixtures. For production, derive them from an observed score record. - Final match outcome settlement should use scores records with
action=game_finalised,statusId=100, andperiod=100. - Fixture examples should expect the backward-compatible
GameStatefield; current values are1for scheduled and6for cancelled.
Common Errors
| Symptom | Likely cause | What to check |
|---|---|---|
| Example script fails before connecting to TxLINE | Node runtime or environment variables do not match the current examples | Use Node.js 20+, set ANCHOR_PROVIDER_URL, ANCHOR_WALLET, and TOKEN_MINT_ADDRESS, then run from the repository root |
504 Gateway Timeout from /api/token/activate | Often a network mismatch, such as a devnet transaction sent to the mainnet activation host, or a backend timeout | Confirm txSig, guest JWT, program ID, and activation URL are all mainnet or all devnet |
403 signature verification failed | Wrong signing preimage, wrong wallet, wrong signature encoding, or wrong JWT host | Sign the exact message string, use the subscribe wallet, send base64 detached signature, and use the matching network JWT |
401 Unauthorized on data endpoints | Missing or expired guest JWT | Request a fresh guest JWT from the same host and retry with the same X-Api-Token |
403 Access denied on data endpoints | Invalid API token, expired subscription, wrong network token, or insufficient subscription permissions | Confirm the X-Api-Token came from the same network and subscription bundle |
Invalid public key input | Placeholder value was not replaced with a real Solana public key | Replace all placeholders with the program ID, TxL mint, and wallet public key for the selected network |
| SSE connection opens but no data messages arrive | The stream can be healthy even when no covered fixture is actively producing updates | Check the Schedule, keep the stream open, or use historical endpoints for completed fixtures |
/api/scores/stat-validation called with seq=0 or a random seq | The sequence was treated as a placeholder instead of coming from a real score record | Get a score record from snapshot, updates, historical data, or the scores stream, then pass that record’s observed Seq or seq value |
InvalidMainTreeProof during validation | Timestamp, epoch day PDA, proof decoding, or fixture/update selection mismatch | Derive daily_scores_roots from the same timestamp passed to validateStat, decode hashes to exactly 32 bytes, and verify you are using the intended fixture and sequence |
validateStatV2 strategy checks the wrong value | statKeys order and positional strategy indexes were mixed up | Keep the requested statKeys order next to the strategy. index, indexA, indexB, and statIndex refer to statsToProve[0..N], not to the numeric stat key itself |
| Final outcome settlement uses the wrong phase | The client selected an in-running or period-specific record instead of the match finalisation record | Use a scores record with action=game_finalised, statusId=100, and period=100 |
Auth Headers
Most data endpoints require both credentials:| Header | Value |
|---|---|
Authorization | Bearer <guest-jwt> from /auth/guest/start |
X-Api-Token | Activated API token from /api/token/activate |
Stream Debugging
Server-Sent Events streams can send connection-level events, heartbeats, or no data for a period when there is no active covered fixture update. When debugging streams:- Use the matching host:
https://txline.txodds.com/api/...for mainnet orhttps://txline-dev.txodds.com/api/...for devnet. - Send both
AuthorizationandX-Api-Token. - Set
Accept: text/event-stream. - Treat an open connection with heartbeats as a live connection, not necessarily a data failure.
- Check Scores Schedule for active or upcoming covered fixtures.
- Use
/api/scores/historical/{fixtureId}for a full replay when the fixture is already historical and within the supported historical window.
Validation Debugging
For score validation, keep the API proof, PDA derivation, and on-chain call aligned:targetTs into validateStat, and make sure every proof hash is decoded to exactly 32 bytes before passing it to Anchor.
Use a real score record sequence for /api/scores/stat-validation; seq=0 is not valid. For settlement conditions tied to a period result, choose a record whose phase or status represents that completed period. An in-running record proves the condition at that moment, not the final result of the period.
For final match outcome settlement, use action=game_finalised records. Current devnet and mainnet releases set both statusId and period to 100 for those records.
For V2 validation, request multiple stats with statKeys=1,2,... and keep that order stable when building statsToProve, statProofs, and strategy predicates. See Runnable Devnet Examples for complete validateStatV2 scripts.
For a first sanity check, use an exact predicate against the returned stat value if your response shape includes validation.statToProve.value:
Support Template
When opening a support ticket, include:- Network: mainnet or devnet.
- Endpoint path and HTTP status.
- Program ID used.
- Service level ID and duration.
- Redacted transaction signature prefix/suffix.
- For activation: the selected leagues array and whether the signed message was
txSig::jwt. - For validation: fixture ID, sequence number, stat key, timestamp used, epoch day, and PDA.
- For V2 validation: the full
statKeyslist and the strategy indexes being checked. - For final outcome validation: whether the selected score record had
action=game_finalised,statusId=100, andperiod=100. - For fixture validation: the fixture
GameStatevalue. - Response body with secrets removed.
- Guest JWT.
X-Api-Token.- Wallet secret key.
- Full private request logs with authorization headers.