Use code JSON50 for 50% off your first month of any plan Code JSON50 - 50% off your first month
Skip to content

Troubleshooting

Common issues and solutions when working with Inodra's Sui infrastructure.

Authentication Issues

Invalid API Key (401)

Symptoms: API requests return 401 Unauthorized

Solutions:

  1. Verify API Key Format

    • Keys should start with indr_
    • Check for extra whitespace or copy-paste errors
  2. Check Dashboard

  3. Correct Header Format

    javascript
    // Primary method (recommended)
    headers: {
      'x-api-key': 'indr_abc123...'
    }
    
    // Alternative (also works)
    headers: {
      'Authorization': 'Bearer indr_abc123...'
    }
    
    // Common mistakes
    headers: {
      'X-API-KEY': 'indr_abc123...', // Wrong! Case sensitive
      'api-key': 'indr_abc123...'     // Wrong! Must be x-api-key
    }

Expired API Key (401)

Symptoms: requests return 401 with the error code API_KEY_EXPIRED (WebSocket close code 4001; gRPC UNAUTHENTICATED with message "API key expired"). A key that used to work stops working on its expiry date.

Solutions:

  1. Open API Keys in the dashboard - expired keys carry an Expired badge and show their expiry date in red
  2. Generate a replacement key and update your application; keys can be set to never expire
  3. Note the distinction: API_KEY_EXPIRED means the key was real but is past its date, while a plain INVALID_API_KEY means the key is unknown, disabled, or mistyped

Insufficient Scope (403)

Symptoms: API requests return 403 Forbidden with the error code INSUFFICIENT_SCOPE; WebSocket connections close with code 4003; gRPC calls fail with PERMISSION_DENIED.

Cause: Every API key carries scopes (Data, Streams, Manage) and the endpoint you called needs one the key doesn't have. For example, chain reads need Data, connecting to Warp streams needs Streams, and webhook/stream management needs Manage.

Solutions:

  1. Open API Keys in the dashboard and click the key's Scopes summary to see and edit its scopes - changes apply within a few seconds
  2. For frontend-embedded keys, keep Manage off and perform management calls server-side with a separate key

See API Key Scopes for the full scope reference.

Rate Limit Exceeded (429)

Symptoms: API returns HTTP 429 Too Many Requests

Solutions:

  1. Check Rate Limit Headers

    bash
    curl -I -H "x-api-key: YOUR_KEY" https://mainnet-api.inodra.com/v1/checkpoints
    
    # Look for these headers:
    # X-RateLimit-Limit: 1000
    # X-RateLimit-Remaining: 50
    # X-RateLimit-Reset: 1640995200
    # Retry-After: 60
  2. Implement Exponential Backoff

    javascript
    async function makeRequestWithRetry(url, options, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        const response = await fetch(url, options)
    
        if (response.status === 429) {
          const retryAfter = response.headers.get('retry-after') || Math.pow(2, i)
          await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000))
          continue
        }
    
        return response
      }
    }
  3. Optimize Request Patterns

    • Use pagination for large datasets
    • Cache frequently accessed data
    • Batch requests when possible
    • Consider upgrading plan for higher limits

Connection Issues

Request Timeouts

Symptoms: Requests timeout or take very long

Solutions:

  1. Increase Timeout Values

    javascript
    const response = await fetch('https://mainnet-api.inodra.com/v1/transactions', {
      headers: { 'x-api-key': 'YOUR_KEY' },
      signal: AbortSignal.timeout(30000) // 30 seconds
    })
  2. Use Pagination

    javascript
    // Request smaller chunks
    const response = await fetch('/v1/transactions?limit=100')
  3. Test Connectivity

    bash
    ping mainnet-api.inodra.com
    curl -I https://mainnet-api.inodra.com/health

DNS Resolution Issues

Symptoms: Cannot resolve mainnet-api.inodra.com

Solutions:

bash
# Test DNS resolution
nslookup mainnet-api.inodra.com
dig mainnet-api.inodra.com

# Try alternative DNS servers
# Google DNS: 8.8.8.8, 8.8.4.4
# Cloudflare DNS: 1.1.1.1, 1.0.0.1

Webhook Issues

Not Receiving Events

Symptoms: Webhooks created but no events received

Solutions:

  1. Test Webhook URL

    bash
    curl -X POST https://your-domain.com/webhook \
      -H "Content-Type: application/json" \
      -d '{"test": true}'
  2. Verify Event Filters

    • Check event type format: package::module::Event
    • Remove sender filter to test broader scope
    • Confirm events are actually occurring on blockchain
  3. Check HTTPS

    • Webhook URLs must use HTTPS in production
    • Verify SSL certificate validity
    • Test: openssl s_client -connect your-domain.com:443

Duplicate Events

Symptoms: Same event delivered multiple times

Cause: Webhooks use at-least-once delivery (normal behavior)

Solution:

javascript
// Implement idempotency
const processedEvents = new Set()

app.post('/webhook', (req, res) => {
  const eventId = req.body.id

  if (processedEvents.has(eventId)) {
    return res.status(200).send('Already processed')
  }

  processedEvents.add(eventId)
  processEvent(req.body)
  res.status(200).send('OK')
})

Processing Timeouts

Symptoms: Webhook deliveries marked as failed

Cause: Endpoint takes > 10 seconds to respond

Solution:

javascript
// Return 200 immediately, process async
app.post('/webhook', (req, res) => {
  // Acknowledge receipt immediately
  res.status(200).send('OK')

  // Process in background
  processWebhookAsync(req.body)
})

async function processWebhookAsync(event) {
  try {
    await processEvent(event)
  } catch (error) {
    console.error('Webhook processing failed:', error)
  }
}

Data Issues

Transaction Not Found (404)

Symptoms: API returns 404 for valid transaction digest

Causes:

  • Transaction very recent (not yet indexed)
  • Invalid digest format

Solutions:

  1. Wait and Retry for recent transactions

    javascript
    async function getTransactionWithRetry(digest, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await fetch(`/v1/transactions/${digest}`)
        } catch (error) {
          if (error.status === 404 && i < maxRetries - 1) {
            await new Promise((resolve) => setTimeout(resolve, 2000))
            continue
          }
          throw error
        }
      }
    }
  2. Validate Digest Format

    javascript
    function isValidTxDigest(digest) {
      return /^[1-9A-HJ-NP-Za-km-z]{44}$/.test(digest)
    }

Response Format Issues

Symptoms: Response doesn't match expected schema

Solutions:

  1. Check Response Headers

    javascript
    const response = await fetch('/v1/checkpoints')
    console.log('Content-Type:', response.headers.get('content-type'))
    console.log('API-Version:', response.headers.get('x-api-version'))
  2. Validate JSON

    javascript
    try {
      const data = await response.json()
    } catch (error) {
      console.error('Invalid JSON:', await response.text())
    }
  3. Check API Documentation

    • Verify endpoint exists and format
    • Review the API Reference for current specifications

Performance Optimization

Slow Response Times

Solutions:

  1. Use Pagination

    javascript
    // Request smaller chunks
    const fetchTransactions = async (address, limit = 100) => {
      let allTxs = []
      let cursor = null
    
      do {
        const params = new URLSearchParams({ limit })
        if (cursor) params.set('cursor', cursor)
    
        const response = await fetch(`/v1/accounts/${address}/transactions?${params}`)
        const data = await response.json()
    
        allTxs.push(...data.data)
        cursor = data.pagination?.cursor
      } while (cursor)
    
      return allTxs
    }
  2. Use Specific Endpoints

    javascript
    // Good: Get only what you need
    const balance = await fetch(`/v1/accounts/${address}/balance`)
    
    // Avoid: Get everything then extract what you need
    const account = await fetch(`/v1/accounts/${address}`)
  3. Implement Caching

    javascript
    const cache = new Map()
    
    async function getCachedData(url, ttl = 60000) {
      const cached = cache.get(url)
    
      if (cached && Date.now() - cached.timestamp < ttl) {
        return cached.data
      }
    
      const data = await fetch(url).then((r) => r.json())
      cache.set(url, { data, timestamp: Date.now() })
    
      return data
    }

Getting Help

Support Channels

  1. Status Page - Check service status
  2. Discord Community - Community support
  3. Support Email - Direct support

Diagnostic Information

When contacting support, include:

  1. Full Request

    bash
    curl -v -H "x-api-key: indr_xxx" \
      "https://mainnet-api.inodra.com/v1/checkpoints"
  2. Error Details

    • Full error response body
    • HTTP status code
    • Timestamp of issue
  3. Environment

    • Operating system
    • Programming language/version
    • Library versions (if applicable)

Released under the MIT License.