Midnight Indexer API Documentation v3
The Midnight Indexer API exposes a GraphQL API that enables clients to query and subscribe to blockchain data—blocks, transactions, contracts, DUST generation, and shielded/unshielded transaction events—indexed from the Midnight blockchain. These capabilities facilitate both historical lookups and real-time monitoring.
Version information:
- Current API version: v3.
- Previous version v1 redirects to v3 automatically.
- Version v2 was skipped during migration.
The examples provided here are illustrative and may need updating if the API changes. Always consider indexer-api/graphql/schema-v3.graphql as the primary source of truth. Adjust queries as necessary to match the latest schema.
GraphQL schema
The GraphQL schema is defined in indexer-api/graphql/schema-v3.graphql. It specifies all queries, mutations, subscriptions, and their types, including arguments and return structures.
Overview of operations
-
Queries: Fetch blocks, transactions, contract actions, and DUST generation status. Examples:
- Retrieve the latest block or a specific block by hash or height.
- Look up transactions by their hash or identifier.
- Inspect the current state of a contract action at a given block or transaction offset.
- Query unshielded token balances held by contracts.
- Query DUST generation status for Cardano stake keys.
-
Mutations: Manage wallet sessions.
connect(viewingKey: ViewingKey!): Creates a session associated with a viewing key.disconnect(sessionId: HexEncoded!): Ends a previously established session.
-
Subscriptions: Receive real-time updates.
blocks: Stream newly indexed blocks.contractActions(address, offset): Stream contract actions.shieldedTransactions(sessionId, ...): Stream shielded transaction updates, including relevant transactions and optional progress updates.unshieldedTransactions(address): Stream unshielded transaction events for a specific address.dustLedgerEvents(id): Stream DUST ledger events.zswapLedgerEvents(id): Stream Zswap ledger events.
API endpoints
HTTP (Queries & Mutations):
POST https://<host>:<port>/api/v3/graphql
Content-Type: application/json
WebSocket (Subscriptions):
wss://<host>:<port>/api/v3/graphql/ws
Sec-WebSocket-Protocol: graphql-transport-ws
Core scalars
HexEncoded: Hex-encoded bytes (for hashes, addresses, session IDs).ViewingKey: A viewing key in hex or Bech32 format for wallet sessions.Unit: An empty return type for mutations that do not return data.UnshieldedAddress: An unshielded address in Bech32m format (e.g.,mn_addr_test1...). Used for unshielded token operations.
Input types
The following input types are used to specify query parameters for blocks, transactions, and contract actions.
BlockOffset (oneOf)
Used to specify a block by either hash or height:
hash: HexEncoded - The block hashheight: Int - The block height
TransactionOffset (oneOf)
Used to specify a transaction by either hash or identifier:
hash: HexEncoded - The transaction hashidentifier: HexEncoded - The transaction identifier
ContractActionOffset (oneOf)
Used to specify a contract action location:
blockOffset: BlockOffset - Query by block (hash or height)transactionOffset: TransactionOffset - Query by transaction (hash or identifier)
Example queries and mutations
These are examples only. Refer to the schema file to confirm exact field names and structures.
block(offset: BlockOffset): Block
Query a block by offset. If no offset is provided, the latest block is returned.
Example:
Query by height:
query {
block(offset: { height: 3 }) {
hash
height
protocolVersion
timestamp
author
parent {
hash
}
transactions {
id
hash
transactionResult {
status
segments {
id
success
}
}
}
}
}
transactions(offset: TransactionOffset!): [Transaction!]!
Fetch transactions by hash or by identifier. Returns an array of transactions matching the criteria.
The fees field is now available on transactions, providing both paidFees and estimatedFees information.
Example (by hash):
query {
transactions(offset: { hash: "3031323..." }) {
id
hash
protocolVersion
merkleTreeRoot
block {
height
hash
}
identifiers
raw
contractActions {
__typename
... on ContractDeploy {
address
state
zswapState
unshieldedBalances {
tokenType
amount
}
}
... on ContractCall {
address
state
entryPoint
zswapState
unshieldedBalances {
tokenType
amount
}
}
... on ContractUpdate {
address
state
zswapState
unshieldedBalances {
tokenType
amount
}
}
}
fees {
paidFees
estimatedFees
}
transactionResult {
status
segments {
id
success
}
}
unshieldedCreatedOutputs {
owner
value
tokenType
intentHash
outputIndex
}
unshieldedSpentOutputs {
owner
value
tokenType
intentHash
outputIndex
}
}
}
Example (by identifier):
query {
transactions(offset: { identifier: "abc123..." }) {
id
hash
unshieldedCreatedOutputs {
owner
value
tokenType
}
unshieldedSpentOutputs {
owner
value
tokenType
}
}
}
contractAction(address: HexEncoded!, offset: ContractActionOffset): ContractAction
Retrieve the latest known contract action at a given offset (by block or transaction). If no offset is provided, returns the latest state.
Example (latest):
query {
contractAction(address: "3031323...") {
__typename
... on ContractDeploy {
address
state
zswapState
unshieldedBalances {
tokenType
amount
}
}
... on ContractCall {
address
state
zswapState
entryPoint
unshieldedBalances {
tokenType
amount
}
}
... on ContractUpdate {
address
state
zswapState
unshieldedBalances {
tokenType
amount
}
}
}
}
Example (by block height):
query {
contractAction(
address: "3031323...",
offset: { blockOffset: { height: 10 } }
) {
__typename
... on ContractDeploy {
address
state
zswapState
unshieldedBalances {
tokenType
amount
}
}
... on ContractCall {
address
state
zswapState
entryPoint
unshieldedBalances {
tokenType
amount
}
}
... on ContractUpdate {
address
state
zswapState
unshieldedBalances {
tokenType
amount
}
}
}
}
dustGenerationStatus(cardanoRewardAddresses: [CardanoRewardAddress!]!): [DustGenerationStatus!]!
Query DUST generation status for one or more Cardano stake keys.
Example:
query {
dustGenerationStatus(
cardanoRewardAddresses: [
"stake_test1uqtgpdz0chm6jnxx7erfd7rhqfud7t4ajazx8es8xk8x3ts06psdv"
]
) {
cardanoRewardAddress
dustAddress
registered
nightBalance
generationRate
currentCapacity
}
}
DUST generation parameters:
- Generation rate: 8,267 Specks per Star per second.
- Maximum capacity: 5 DUST per NIGHT.
- The
registeredfield indicates if stake key is registered via NativeTokenObservation pallet. - Registration data comes from Cardano mainnet via bridge.
Important note on currentCapacity:
The currentCapacity field represents the maximum DUST generation capacity based on the Night UTXO balance and elapsed time. This value:
- Is accurate until the first DUST fee payment.
- May be higher than actual balance after fee payments.
- Cannot track spent DUST (fee payments are shielded transactions).
For accurate DUST balance after fee payments, query the connected wallet directly via wallet SDK or DApp Connector API. Use currentCapacity as an approximation when wallet connection is unavailable.
Contract action types
All ContractAction types (ContractDeploy, ContractCall, ContractUpdate) implement the ContractAction interface with these common fields:
address: The contract address (HexEncoded)state: The contract state (HexEncoded)zswapState: The contract-specific zswap state at this action (HexEncoded)transaction: The transaction that contains this action
Contract actions can be one of three types:
- ContractDeploy: Initial contract deployment
- ContractCall: Invocation of a contract's entry point
- ContractUpdate: State update to an existing contract
Each type implements the ContractAction interface but may have additional fields. For example, ContractCall includes an entryPoint field and a reference to its associated deploy.
All contract action types include an unshieldedBalances field that returns the token balances held by the contract:
- ContractDeploy: Always returns empty balances (contracts are deployed with zero balance).
- ContractCall: Returns balances after the call execution (may be modified by
unshielded_inputs/unshielded_outputs). - ContractUpdate: Returns balances after the maintenance update.
ContractBalance Type
type ContractBalance {
tokenType: HexEncoded! # Token type identifier
amount: String! # Balance amount (supports u128 values)
}
Block type
The Block type represents a blockchain block:
hash: The block hash (HexEncoded)height: The block height (Int!)protocolVersion: The protocol version (Int!)timestamp: The UNIX timestamp (Int!)author: The block author (HexEncoded, optional)parent: Reference to the parent block (Block, optional)transactions: Array of transactions within this block ([Transaction!]!)
Transaction type
The Transaction type represents a blockchain transaction with its associated data:
id: The transaction ID (Int!)hash: The transaction hash (HexEncoded)protocolVersion: The protocol version (Int!)transactionResult: The result of applying the transaction to the ledger statefees: Fee information including both paid and estimated feesidentifiers: Transaction identifiers array ([HexEncoded!]!)raw: The raw transaction content (HexEncoded)merkleTreeRoot: The merkle-tree root (HexEncoded)block: Reference to the block containing this transactioncontractActions: Array of contract actions within this transactionunshieldedCreatedOutputs: UTXOs created by this transactionunshieldedSpentOutputs: UTXOs spent by this transaction
TransactionResult type
The result of applying a transaction to the ledger state:
status: TransactionResultStatus (SUCCESS, PARTIAL_SUCCESS, or FAILURE)segments: Optional array of segment results for partial success cases
TransactionFees type
Fee information for a transaction:
paidFees: The actual fees paid for this transaction in DUST (String)estimatedFees: The estimated fees that were calculated for this transaction in DUST (String)
Unshielded token types
Unshielded tokens are publicly visible on-chain assets that can be tracked and queried. The following types represent unshielded UTXOs and their associated data.
UnshieldedUtxo
Represents an unshielded UTXO (Unspent Transaction Output):
owner: The owner's address in Bech32m formatintentHash: The hash of the intent that created this output (HexEncoded)value: The UTXO value as a string (to support u128)tokenType: The token type identifier (HexEncoded)outputIndex: The index of this output within its creating transactioncreatedAtTransaction: Reference to the transaction that created this UTXOspentAtTransaction: Reference to the transaction that spent this UTXO (null if unspent)
DUST generation types
The following types provide information about DUST generation status, capacity, and related ledger events.
DustGenerationStatus
DUST generation status for a Cardano stake key:
cardanoRewardAddress: The Bech32-encoded Cardano stake address (e.g., stake_test1... or stake1...)dustAddress: Associated DUST address if registered (HexEncoded, optional)registered: Whether this stake key is registered (Boolean!)nightBalance: NIGHT balance backing generation (String)generationRate: Generation rate in Specks per second (String)currentCapacity: Current DUST generation capacity in Specks - represents maximum possible balance, may be higher than actual balance after fee payments (String)
DustLedgerEvent
DUST ledger events include:
DustInitialUtxo: Initial DUST UTXO creation eventDustGenerationDtimeUpdate: DUST generation decay time updateDustSpendProcessed: DUST spend processing eventParamChange: DUST parameter change event
All DUST ledger event types share common fields:
id: Event ID (Int!)raw: Raw event data (HexEncoded)maxId: Maximum ID of all DUST events (Int!)
Mutations
Mutations allow the client to connect a wallet (establishing a session) and disconnect it.
connect(viewingKey: ViewingKey!): HexEncoded!
Establishes a session for a given wallet viewing key in either bech32m or hex format. Returns the session ID.
Viewing key format support:
- Bech32m (preferred): A base-32 encoded format with a human-readable prefix, e.g.,
mn_shield-esk_dev1.... - Hex (fallback): A hex-encoded string representing the key bytes.
Example:
mutation {
# Provide the bech32m format:
connect(viewingKey: "mn_shield-esk1abcdef...")
}
Response:
{
"data": {
"connect": "sessionIdHere"
}
}
Use this sessionId for shielded transactions subscriptions.
disconnect(sessionId: HexEncoded!): Unit!
Ends an existing session.
Example:
When done:
mutation {
disconnect(sessionId: "sessionIdHere")
}
Subscriptions: Real-time Updates
Subscriptions use a WebSocket connection following the GraphQL over WebSocket protocol. After connecting and sending a connection_init message, the client can start subscription operations.
Blocks subscription
blocks(offset: BlockOffset): Block!
Subscribe to new blocks. The offset parameter lets you start receiving from a given block (by height or hash). If omitted, starts from the latest block.
Example:
{
"id": "1",
"type": "start",
"payload": {
"query": "subscription { blocks(offset: { height: 10 }) { hash height protocolVersion timestamp author parent { hash } transactions { id hash } } }"
}
}
When a new block is indexed, the client receives a next message.
Contract actions subscription
contractActions(address: HexEncoded!, offset: BlockOffset): ContractAction!
Subscribes to contract actions for a particular address. New contract actions (calls, updates) are pushed as they occur.
Example:
{
"id": "2",
"type": "start",
"payload": {
"query": "subscription { contractActions(address:\"3031323...\", offset: { height: 1 }) { __typename ... on ContractDeploy { address state zswapState unshieldedBalances { tokenType amount } } ... on ContractCall { address state zswapState entryPoint unshieldedBalances { tokenType amount } } ... on ContractUpdate { address state zswapState unshieldedBalances { tokenType amount } } } }"
}
}
Shielded transactions subscription
shieldedTransactions(sessionId: HexEncoded!, index: Int, sendProgressUpdates: Boolean): ShieldedTransactionsEvent!
Subscribes to shielded transaction updates. This includes relevant transactions and possibly Merkle tree updates, as well as ShieldedTransactionsProgress events if sendProgressUpdates is set to true, which is also the default. The index parameter can be used to resume from a certain point.
Adjust index and offset arguments as needed.
Example:
{
"id": "3",
"type": "start",
"payload": {
"query": "subscription { shieldedTransactions(sessionId: \"1CYq6ZsLmn\", index: 100) { __typename ... on ViewingUpdate { index update { __typename ... on MerkleTreeCollapsedUpdate { start end update protocolVersion } ... on RelevantTransaction { start end transaction { id hash } } } } ... on ShieldedTransactionsProgress { highestIndex highestRelevantIndex highestRelevantWalletIndex } } }"
}
}
Event types:
ShieldedTransactionsEvent (union type):
ViewingUpdate: Contains relevant transactions and/or collapsed Merkle tree updatesindex: Next start index into the zswap state (Int!)update: Array of ZswapChainStateUpdate items ([ZswapChainStateUpdate!]!)MerkleTreeCollapsedUpdate: Merkle tree updatestart: Start index (Int!)end: End index (Int!)update: Hex-encoded merkle-tree collapsed update (HexEncoded)protocolVersion: Protocol version (Int!)
RelevantTransaction: Transaction relevant to the walletstart: Start index (Int!)end: End index (Int!)transaction: The relevant transaction (Transaction!)
ShieldedTransactionsProgress: Synchronization progress informationhighestIndex: The highest end index of all currently known transactions (Int!)highestRelevantIndex: The highest end index of all currently known relevant transactions (Int!)highestRelevantWalletIndex: The highest end index for this particular wallet (Int!)
Unshielded transactions subscription
unshieldedTransactions(address: UnshieldedAddress!, transactionId: Int): UnshieldedTransactionsEvent!
Subscribes to unshielded transaction events for a specific address. Emits events whenever transactions involve unshielded UTXOs for the given address.
Parameters:
address: The unshielded address to monitor (must be in Bech32m format).transactionId: Optional. The transaction ID to start from (defaults to 0).
Example:
{
"id": "4",
"type": "start",
"payload": {
"query": "subscription { unshieldedTransactions(address: \"mn_addr_test1...\") { __typename ... on UnshieldedTransaction { transaction { hash block { height } } createdUtxos { owner value tokenType intentHash outputIndex } spentUtxos { owner value tokenType intentHash outputIndex } } ... on UnshieldedTransactionsProgress { highestTransactionId } } }"
}
}
Event types:
- UnshieldedTransaction: When UTXOs are created or spent, includes transaction details and affected UTXOs
- UnshieldedTransactionsProgress: Periodic synchronization progress updates
UnshieldedTransactionsEvent
Event payload for the unshielded transaction subscription:
UnshieldedTransaction: Contains transaction details and UTXOs created/spenttransaction: The transaction that created and/or spent UTXOscreatedUtxos: UTXOs created in this transaction for the subscribed addressspentUtxos: UTXOs spent in this transaction for the subscribed address
UnshieldedTransactionsProgress: Progress informationhighestTransactionId: The highest transaction ID of all currently known transactions for the subscribed address
DUST ledger events subscription
dustLedgerEvents(id: Int): DustLedgerEvent!
Subscribe to DUST ledger events. The id parameter allows resuming from a specific event.
Example:
{
"id": "5",
"type": "start",
"payload": {
"query": "subscription { dustLedgerEvents { id __typename ... on DustInitialUtxo { output { nonce } } raw maxId } }"
}
}
Zswap ledger events subscription
zswapLedgerEvents(id: Int): ZswapLedgerEvent!
Subscribe to Zswap ledger events. The id parameter allows resuming from a specific event.
Example:
{
"id": "6",
"type": "start",
"payload": {
"query": "subscription { zswapLedgerEvents { id raw maxId } }"
}
}
Query limits configuration
The server may apply limitations to queries (e.g. max-depth, max-fields, timeout, and complexity cost). Requests that violate these limits return errors indicating the reason (too many fields, too deep, too costly, or timed out).
Example error:
{
"data": null,
"errors": [
{
"message": "Query has too many fields: 20. Max fields: 10."
}
]
}
Authentication
Shielded transactions subscription requires a sessionId from the connect mutation.
Regenerating the Schema
If you modify the code defining the GraphQL schema, regenerate it:
just generate-indexer-api-schema
This ensures the schema file stays aligned with code changes.
Migration from v1
If migrating from API v1:
- Update endpoint URLs from
/v1/graphqlto/v3/graphql(though v1 redirects automatically). - Review field name changes (e.g.,
chainState→zswapStatein contract actions). - Test thoroughly as some response structures may have evolved.
Conclusion
This document offers a few hand-picked examples and an overview of available operations. For the most accurate and comprehensive reference, consult the schema file. As the API evolves, remember to validate these examples against the schema and update them as needed.