Chat with us, powered by LiveChat
← Return to MyDisct Solver

Castle Token Solving

Bypass Castle fraud and bot protection and generate valid request tokens for protected web applications. Our solver handles the Castle client fingerprinting pipeline and returns tokens that satisfy Castle server-side validation requirements.

What is Castle Token?

Castle is a fraud protection and device intelligence platform used by fintech, e-commerce, and authentication-sensitive applications. It works by loading a JavaScript SDK on the client that collects device fingerprinting data, behavioral signals, and network characteristics. This telemetry is submitted to Castle's API to generate a request token. This token is then included in subsequent API requests, where Castle's server-side library validates it to determine whether the request originates from a legitimate browser session. Our solver replicates the full Castle SDK execution flow and returns a valid request token that passes server-side validation.

Captcha Type

Use the following captcha type identifier in your API requests:

"type": "CASTLE_TOKEN"

Request Format

POST /createTask

Request Parameters

Parameter Type Required Description
auth.token string required Your API key
captcha.type string required Must be "CASTLE_TOKEN"
captcha.metadata.siteUrl string required The full URL of the page where the Castle SDK is loaded
captcha.metadata.siteKey string optional The Castle publishable key (found in the site's JavaScript configuration where Castle is initialized, e.g., Castle.configure({ pk: '...' })).
captcha.payload.userAgent string optional Your browser user agent string. Castle embeds this in its telemetry payload.
captcha.payload.proxy object recommended Proxy configuration object. Recommended for consistency — the Castle token carries IP reputation signals.
captcha.payload.proxy.protocol string required* Proxy protocol: "http", "https", "socks4", or "socks5"
captcha.payload.proxy.host string required* Proxy IP address or hostname
captcha.payload.proxy.port number required* Proxy port number
captcha.payload.proxy.username string optional Proxy authentication username
captcha.payload.proxy.password string optional Proxy authentication password

* Required only if the parent object (proxy) is provided

Finding the Castle Publishable Key

The Castle publishable key is typically set in the site's JavaScript as part of the Castle SDK initialization call. Search the page source for Castle.configure or _castle("setAppId" and the key will be the string value passed there. It typically starts with a prefix like pk_ or is a UUID-style identifier. Providing this value as siteKey improves solving accuracy.

Example Request

JSON
{
  "auth": {
    "token": "YOUR_API_KEY"
  },
  "context": {
    "source": "api",
    "version": "1.0.0"
  },
  "captcha": {
    "type": "CASTLE_TOKEN",
    "metadata": {
      "siteUrl": "https://example.com/login",
      "siteKey": "pk_AbCdEfGhIjKlMnOpQrStUvWxYz123456"
    },
    "payload": {
      "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      "proxy": {
        "protocol": "http",
        "host": "1.2.3.4",
        "port": 8080,
        "username": "proxyuser",
        "password": "proxypass"
      }
    }
  }
}
JavaScript
const response = await fetch('https://solver-api.mydisct.com/createTask', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    auth: {
      token: 'YOUR_API_KEY'
    },
    context: {
      source: 'api',
      version: '1.0.0'
    },
    captcha: {
      type: 'CASTLE_TOKEN',
      metadata: {
        siteUrl: 'https://example.com/login',
        siteKey: 'pk_AbCdEfGhIjKlMnOpQrStUvWxYz123456'
      },
      payload: {
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        proxy: {
          protocol: 'http',
          host: '1.2.3.4',
          port: 8080,
          username: 'proxyuser',
          password: 'proxypass'
        }
      }
    }
  })
});

const data = await response.json();
console.log('Task ID:', data.task.id);
Python
import requests

response = requests.post(
    'https://solver-api.mydisct.com/createTask',
    headers={
        'Content-Type': 'application/json',
        'apikey': 'YOUR_API_KEY'
    },
    json={
        'auth': {'token': 'YOUR_API_KEY'},
        'context': {'source': 'api', 'version': '1.0.0'},
        'captcha': {
            'type': 'CASTLE_TOKEN',
            'metadata': {
                'siteUrl': 'https://example.com/login',
                'siteKey': 'pk_AbCdEfGhIjKlMnOpQrStUvWxYz123456'
            },
            'payload': {
                'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                'proxy': {
                    'protocol': 'http',
                    'host': '1.2.3.4',
                    'port': 8080,
                    'username': 'proxyuser',
                    'password': 'proxypass'
                }
            }
        }
    }
)

data = response.json()
print('Task ID:', data['task']['id'])
cURL
curl -X POST https://solver-api.mydisct.com/createTask \
  -H "Content-Type: application/json" \
  -H "apikey: YOUR_API_KEY" \
  -d '{
    "auth": {"token": "YOUR_API_KEY"},
    "context": {"source": "api", "version": "1.0.0"},
    "captcha": {
      "type": "CASTLE_TOKEN",
      "metadata": {
        "siteUrl": "https://example.com/login",
        "siteKey": "pk_AbCdEfGhIjKlMnOpQrStUvWxYz123456"
      },
      "payload": {
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "proxy": {
          "protocol": "http",
          "host": "1.2.3.4",
          "port": 8080,
          "username": "proxyuser",
          "password": "proxypass"
        }
      }
    }
  }'

Response Format

Create Task Response (Processing)

{
  "success": true,
  "service": "MyDisct Solver",
  "message": "Captcha task created successfully",
  "task": {
    "id": "MyDisctSolver_abc123",
    "status": "processing"
  }
}

Fetch Result Response (Completed)

{
  "success": true,
  "service": "MyDisct Solver",
  "message": "Castle challenge solved successfully",
  "task": {
    "id": "MyDisctSolver_abc123",
    "status": "completed",
    "result": {
      "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdHgiOiJ...",
      "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      "timestamp": "2025-10-10T12:00:00.000Z"
    }
  }
}

Response Fields

Field Type Description
result.token string The Castle request token. This is the value returned by Castle.createRequestToken() in a legitimate browser session. Pass it as the X-Castle-Request-Token header.
result.user_agent string The exact user agent used during solving. Forward this in your requests to maintain consistency with the token payload.
How to Use the Castle Token

Castle tokens are submitted as the value of the X-Castle-Request-Token HTTP header in API requests. The Castle server-side SDK validates this header by calling castle.risk() or castle.filter() with the token value. If the token is valid and the associated risk score is acceptable, the request proceeds normally. Always send the token alongside the matching user agent.

Implementation Guide

Complete JavaScript Implementation

JavaScript
async function solveCastleToken(siteUrl, siteKey, options = {}) {
  const createResponse = await fetch('https://solver-api.mydisct.com/createTask', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'apikey': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      auth: { token: 'YOUR_API_KEY' },
      context: { source: 'api', version: '1.0.0' },
      captcha: {
        type: 'CASTLE_TOKEN',
        metadata: { siteUrl, siteKey },
        payload: {
          userAgent: options.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
          proxy: options.proxy
        }
      }
    })
  });

  const createData = await createResponse.json();
  if (!createData.success) throw new Error(createData.error.message);

  const taskId = createData.task.id;

  while (true) {
    await new Promise(resolve => setTimeout(resolve, 5000));

    const resultResponse = await fetch('https://solver-api.mydisct.com/fetchResult', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'apikey': 'YOUR_API_KEY'
      },
      body: JSON.stringify({ taskId })
    });

    const resultData = await resultResponse.json();
    if (resultData.task.status === 'completed') {
      return resultData.task.result;
    } else if (resultData.task.status === 'failed') {
      throw new Error('Castle token solving failed');
    }
  }
}

async function loginWithCastleToken(credentials, castleResult) {
  const response = await fetch('https://example.com/api/auth/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': castleResult.user_agent,
      'X-Castle-Request-Token': castleResult.token,
      'Accept': 'application/json'
    },
    body: JSON.stringify(credentials)
  });

  return await response.json();
}

const castleResult = await solveCastleToken(
  'https://example.com/login',
  'pk_AbCdEfGhIjKlMnOpQrStUvWxYz123456',
  {
    proxy: {
      protocol: 'http',
      host: '1.2.3.4',
      port: 8080,
      username: 'proxyuser',
      password: 'proxypass'
    }
  }
);

console.log('Castle token:', castleResult.token.substring(0, 50) + '...');

const loginResult = await loginWithCastleToken(
  { email: '[email protected]', password: 'secretpassword' },
  castleResult
);

console.log('Login result:', loginResult);

Python Implementation

Python
import requests
import time

def solve_castle_token(site_url, site_key, api_key, user_agent=None, proxy_config=None):
    create_response = requests.post(
        'https://solver-api.mydisct.com/createTask',
        headers={
            'Content-Type': 'application/json',
            'apikey': api_key
        },
        json={
            'auth': {'token': api_key},
            'context': {'source': 'api', 'version': '1.0.0'},
            'captcha': {
                'type': 'CASTLE_TOKEN',
                'metadata': {
                    'siteUrl': site_url,
                    'siteKey': site_key
                },
                'payload': {
                    'userAgent': user_agent or 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'proxy': proxy_config
                }
            }
        }
    )

    create_data = create_response.json()
    if not create_data['success']:
        raise Exception(create_data['error']['message'])

    task_id = create_data['task']['id']
    print(f'Task created: {task_id}')

    while True:
        time.sleep(5)

        result_response = requests.post(
            'https://solver-api.mydisct.com/fetchResult',
            headers={
                'Content-Type': 'application/json',
                'apikey': api_key
            },
            json={'taskId': task_id}
        )

        result_data = result_response.json()
        if result_data['task']['status'] == 'completed':
            return result_data['task']['result']
        elif result_data['task']['status'] == 'failed':
            raise Exception('Castle token solving failed')

        print('Waiting for solution...')

castle_result = solve_castle_token(
    site_url='https://example.com/login',
    site_key='pk_AbCdEfGhIjKlMnOpQrStUvWxYz123456',
    api_key='YOUR_API_KEY',
    proxy_config={
        'protocol': 'http',
        'host': '1.2.3.4',
        'port': 8080,
        'username': 'proxyuser',
        'password': 'proxypass'
    }
)

print(f'Castle token: {castle_result["token"][:50]}...')

login_response = requests.post(
    'https://example.com/api/auth/login',
    headers={
        'Content-Type': 'application/json',
        'User-Agent': castle_result['user_agent'],
        'X-Castle-Request-Token': castle_result['token'],
        'Accept': 'application/json'
    },
    json={
        'email': '[email protected]',
        'password': 'secretpassword'
    }
)

print(f'Login status: {login_response.status_code}')
print(f'Login result: {login_response.json()}')

Best Practices

Recommendations
  • Provide the Publishable Key: Extract the Castle siteKey from the page source and provide it — this ensures the solver targets the correct Castle configuration for the site
  • Use the Correct Header: Pass the token as the X-Castle-Request-Token header — this is the standard Castle integration pattern
  • IP and User Agent Consistency: Castle embeds both IP and user agent signals in its tokens — use the same proxy and forward the exact user_agent from the result
  • Residential Proxies: Castle scores IP reputation as part of its risk assessment — residential proxies produce better risk scores than datacenter IPs
  • Token Freshness: Castle tokens are single-use in many implementations. Generate a new token for each protected request rather than reusing one
  • Polling Interval: Use 5-second polling intervals — Castle token generation typically completes in 5 to 15 seconds
  • Combine with Session Cookies: Some Castle-protected sites also require valid session cookies alongside the token — ensure your session is properly initialized before submitting

Common Issues

Issue: Request denied with "invalid token" error

Solution: Ensure the token is being sent as the X-Castle-Request-Token header. Also confirm that the same proxy IP and user agent used during solving are being forwarded in the request.

Issue: High risk score causing request to be blocked

Solution: Switch to a residential proxy. Castle's risk model penalizes datacenter and VPN IP ranges. Residential proxies provide the IP reputation signals that Castle uses to assign lower risk scores.

Issue: Token appears expired immediately

Solution: Castle tokens have a short validity window. Use the token within a few seconds of receiving it. Do not store tokens for later use — generate a fresh one immediately before each protected request.