Skip to main content

Troubleshooting Guide

This guide covers the most common issues encountered during Freeq API integration and their solutions.

Authentication Issues

401 Unauthorized Error

This is the most common issue during setup. A 401 error indicates a problem with JWT validation.

Systematic Debugging Approach

Step 1: Verify OIDC Discovery Document

curl https://your-domain.com/.well-known/openid-configuration

Expected response:

  • 200 OK status
  • Valid JSON with required fields
  • Publicly accessible (no authentication required)

Common issues:

  • Document not publicly accessible
  • Missing required fields (jwks_uri, issuer)
  • HTTPS certificate issues

Step 2: Check JWKS Endpoint

curl https://your-domain.com/.well-known/jwks.json

Expected response:

{
"keys": [
{
"kid": "key-id",
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}

Common issues:

  • keys is not an array (most frequent error)
  • Missing required key properties
  • Incorrect key format
  • Keys don't match your signing key

Step 3: Validate JWT Claims

Use jwt.io to decode your JWT and verify:

Required claims:

{
"iss": "https://your-domain.com", // Must match registered domain
"aud": "api.freeq.com", // Must be exactly this value
"exp": 1642291200, // Valid expiration timestamp
"email": "user@example.com" // User's email address
}

Common issues:

  • aud claim incorrect or missing
  • email claim missing
  • iss doesn't match OIDC discovery document
  • Token expired (exp in the past)

Quick Fixes for 401 Errors

  1. JWKS Format Error

    // Wrong - keys is not an array
    {
    "keys": {
    "kid": "key-id",
    "kty": "RSA"
    }
    }

    // Correct - keys must be an array
    {
    "keys": [
    {
    "kid": "key-id",
    "kty": "RSA",
    "n": "...",
    "e": "AQAB"
    }
    ]
    }
  2. Missing Audience Claim

    // Add this to your JWT payload
    {
    "aud": "api.freeq.com"
    }
  3. Email Claim Missing

    // Ensure email is included
    {
    "email": "user@example.com"
    }

400 Bad Request Error

Common Causes

Missing Project Key

// Request body must include your project key
{
"key": "YOUR_PROJECT_KEY_HERE"
}

Malformed JSON

  • Check for trailing commas
  • Verify proper JSON syntax
  • Ensure Content-Type header is set to application/json

Invalid Project Key

  • Verify you're using the correct key for your environment (testnet vs production)
  • Ensure no extra whitespace or characters in the key
  • Check that the key matches what was provided by the Freeq team

Webhook Issues

Webhooks Not Being Received

Debugging Steps

Step 1: Verify Endpoint Accessibility

# Test your webhook endpoint externally
curl -X POST https://your-domain.com/webhooks/freeq \
-H 'Content-Type: application/json' \
-d '{"test": "webhook"}'

Step 2: Check Response Codes Your webhook endpoint must return a 2xx status code:

app.post('/webhooks/freeq', (req, res) => {
try {
// Process webhook
processWebhook(req.body);

// Always return 2xx status
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
// Even on error, consider returning 2xx if you want to prevent retries
res.status(200).json({ error: error.message });
}
});

Step 3: Enable Webhook Logging

app.post('/webhooks/freeq', (req, res) => {
console.log('Webhook received:', {
timestamp: new Date().toISOString(),
headers: req.headers,
body: req.body
});

// Your processing logic here
res.status(200).json({ received: true });
});

Common Webhook Issues

HTTPS Required in Production

  • Webhook endpoints must use HTTPS in production
  • Use tools like Let's Encrypt for free SSL certificates
  • Test with ngrok for local development

Timeout Issues

// Set reasonable timeouts for webhook processing
app.use(timeout('10s'));

app.post('/webhooks/freeq', (req, res) => {
// Process quickly and acknowledge
res.status(200).json({ received: true });

// Do heavy processing asynchronously
setImmediate(() => {
processWebhookAsync(req.body);
});
});

URL Misconfiguration

  • Verify the exact URL provided to Freeq team
  • Check for trailing slashes, incorrect paths
  • Test the URL manually before providing it

Webhook Retry Logic

If webhooks are failing, they'll be retried when the user next calls /signin:

// Trigger webhook retry by calling signin
async function retryFailedWebhook(userJwt, projectKey) {
try {
const response = await axios.post(
'https://api.testnet.superfreeq.com/api/v1/signin',
{ key: projectKey },
{
headers: {
'Authorization': `Bearer ${userJwt}`,
'Content-Type': 'application/json',
},
}
);

console.log('Signin call completed, webhook retry triggered if needed');
return response.data;
} catch (error) {
console.error('Failed to trigger webhook retry:', error);
throw error;
}
}

Integration Flow Issues

Wallet Creation Not Working

For Async Flow

Issue: Called /createasync but no wallet created

Debugging:

  1. Check if you received a 202 Accepted response
  2. Verify webhook endpoint is working
  3. Try calling /signin to trigger retry
  4. Check server logs for any processing errors

Solution:

// Robust async wallet creation
async function createWalletAsync(userJwt, projectKey, userId) {
try {
const response = await axios.post(
'https://api.testnet.superfreeq.com/api/v1/wallets/createasync',
{
key: projectKey,
token: `user-${userId}` // Help identify user in webhook
},
{
headers: {
'Authorization': `Bearer ${userJwt}`,
'Content-Type': 'application/json',
},
timeout: 15000, // 15 second timeout
}
);

if (response.status === 202) {
console.log(`Wallet creation requested for user ${userId}`);
return { status: 'pending', message: 'Wallet creation in progress' };
}
} catch (error) {
console.error('Async wallet creation failed:', error);
throw error;
}
}

For Sync Flow

Issue: /signin not creating wallet for new users

Debugging:

  1. Verify JWT claims include email
  2. Check if user actually needs a new wallet
  3. Review response for error details

Solution:

// Sync wallet creation/retrieval
async function getOrCreateWallet(userJwt, projectKey) {
try {
const response = await axios.post(
'https://api.testnet.superfreeq.com/api/v1/signin',
{ key: projectKey },
{
headers: {
'Authorization': `Bearer ${userJwt}`,
'Content-Type': 'application/json',
},
timeout: 30000, // Longer timeout for wallet creation
}
);

return response.data;
} catch (error) {
console.error('Wallet creation/retrieval failed:', error);
throw error;
}
}

Duplicate Wallet Issues

Issue: Concerned about creating duplicate wallets

Solution: The system is idempotent based on email address. Multiple calls for the same user won't create duplicate wallets.

// Safe to call multiple times for same user
await createWalletAsync(userJwt, projectKey, userId);
await createWalletAsync(userJwt, projectKey, userId); // Won't create duplicate

Development Environment Issues

Local Testing Problems

Webhook Testing with ngrok

# Install ngrok
npm install -g ngrok

# Expose local port
ngrok http 3000

# Use the HTTPS URL for webhook configuration
# Example: https://abc123.ngrok.io/webhooks/freeq

Environment Variables

# .env file for development
FREEQ_PROJECT_KEY=your_dev_project_key
FREEQ_API_URL=https://api.testnet.superfreeq.com
WEBHOOK_SECRET=your_webhook_secret

# Load in your application
require('dotenv').config();

CORS Issues in Development

// If calling from frontend (not recommended for production)
const cors = require('cors');
app.use(cors({
origin: ['http://localhost:3000', 'https://your-domain.com'],
credentials: true
}));

JWT Testing Issues

Generate Test JWTs

const jwt = require('jsonwebtoken');
const fs = require('fs');

// Read your private key
const privateKey = fs.readFileSync('path/to/your/private-key.pem');

// Generate test JWT
const testJWT = jwt.sign(
{
iss: 'https://your-domain.com',
aud: 'api.freeq.com',
exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
email: 'test@example.com',
sub: 'test-user-123'
},
privateKey,
{ algorithm: 'RS256', keyid: 'your-key-id' }
);

console.log('Test JWT:', testJWT);

Validate JWT Locally

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

const client = jwksClient({
jwksUri: 'https://your-domain.com/.well-known/jwks.json'
});

function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}

// Verify JWT
jwt.verify(token, getKey, {
audience: 'api.freeq.com',
issuer: 'https://your-domain.com',
algorithms: ['RS256']
}, (err, decoded) => {
if (err) {
console.error('JWT validation failed:', err);
} else {
console.log('JWT valid:', decoded);
}
});

Production Issues

Performance Optimization

Connection Pooling

// Use HTTP agent for connection reuse
const https = require('https');
const axios = require('axios');

const agent = new https.Agent({
keepAlive: true,
maxSockets: 10
});

const freeqAPI = axios.create({
baseURL: 'https://api.testnet.superfreeq.com',
httpsAgent: agent,
timeout: 10000
});

Rate Limiting

// Implement rate limiting for API calls
const rateLimit = require('express-rate-limit');

const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});

app.use('/api/', apiLimiter);

Error Monitoring

Logging Best Practices

const winston = require('winston');

const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});

// Log Freeq API calls
async function loggedFreeqCall(endpoint, data, userJwt) {
const startTime = Date.now();

try {
const response = await freeqAPI.post(endpoint, data, {
headers: { 'Authorization': `Bearer ${userJwt}` }
});

logger.info('Freeq API success', {
endpoint,
duration: Date.now() - startTime,
status: response.status
});

return response.data;
} catch (error) {
logger.error('Freeq API error', {
endpoint,
duration: Date.now() - startTime,
error: error.message,
status: error.response?.status,
data: error.response?.data
});

throw error;
}
}

Frequently Asked Questions

What happens if I call /createasync for a user who already has a wallet?

The system is idempotent based on the user's email address. If a wallet already exists, the system acknowledges the request without creating a duplicate. No error is returned.

Can I test the API without implementing full JWT authentication?

For initial testing, you can use the interactive Swagger documentation which handles authentication through Auth0. However, for your application integration, you must implement proper JWT authentication.

How long does wallet creation take?

Asynchronous wallet creation typically takes 30 seconds to 2 minutes. Synchronous creation (via /signin) usually completes within 10-30 seconds but may take longer during high load periods.

What should I do if webhooks are consistently failing?

  1. Check your webhook endpoint logs for errors
  2. Verify you're returning 2xx status codes
  3. Test the endpoint manually with curl
  4. Contact Freeq support with your webhook URL and error details

Can I change my project key after integration?

Project keys can be rotated, but this requires coordination with the Freeq team. Contact support to schedule key rotation to avoid service interruption.

Getting Additional Help

Before Contacting Support

  1. Complete the prerequisites checklist
  2. Test each component individually (OIDC, JWKS, JWT)
  3. Check your application logs for specific error messages
  4. Try the solutions in this troubleshooting guide

Information to Include When Requesting Help

  • Error messages (exact text and HTTP status codes)
  • Your issuer domain and OIDC discovery document URL
  • Sample JWT (with sensitive data redacted)
  • Webhook endpoint URL (if using webhooks)
  • Steps you've already tried from this troubleshooting guide

Contact Information

Additional Resources


Still Having Issues?

If you've worked through this troubleshooting guide and are still experiencing problems, don't hesitate to contact the Freeq team. We're here to help ensure your integration is successful.