> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentpay.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Validating User API Keys

> Use the `validate_api_key()` method to authenticate User API Keys and understand its use cases before charging for usage.

While the `consume()` method inherently validates a User API Key before attempting a charge, we recommend validating the API Key proactively before processing incoming requests. There are two places where you might choose to do so:

* (Recommended) Immediately after User API Key extraction (e.g. in middleware).
* Inside your MCP Server feature handlers, before processing the core business logic.

<Note>
  This guide assumes that you have already successfully extracted the User API Key from the HTTP request. To learn more about that, please refer to our [Extracting API Keys](./extracting-api-keys) guide.
</Note>

## The `validate_api_key()` Method

This method checks the status of a given User API Key with the AgentPay system.

### Parameters:

* `api_key` (str): **Required.** The User API Key to be validated.

### Returns:

The `validate_api_key()` method returns a `ValidationResult` object with the following attributes:

* `is_valid` (bool): `True` if the API key is valid, active, and recognized by AgentPay. `False` otherwise.
* `invalid_reason` (str | None): If `is_valid` is `False`, provides the reason for invalidity. Possible values:
  * `"invalid_key"`: The key is not recognized or has been revoked/deactivated
  * `"outstanding_payment"`: The key is associated with an account that has an unpaid consumption attempt from a previous failed `consume()` call due to insufficient funds. The key will be automatically reactivated once the user adds sufficient funds to their account.
  * `"insufficient_balance"`: The key is valid but the associated account has no remaining balance (including free credits)
  * `None` if `is_valid` is `True`

<Note>
  The `"outstanding_payment"` state is a temporary condition that occurs when a user attempts to consume more than their available balance. Once they add sufficient funds to their account, the outstanding payment will be automatically processed and their API key will be reactivated.
</Note>

## Example Usage

```python theme={null}
from agentpay_sdk import AgentPayClient
from mcp.server.fastmcp import FastMCP
from contextvars import ContextVar

# Initialize AgentPay client
agentpay_client = AgentPayClient(service_token="YOUR_SERVICE_TOKEN")

# Create FastMCP instance
mcp = FastMCP("your-server-name")

# Define context variable for API key
api_key_context: ContextVar[str | None] = ContextVar("api_key_context", default=None)

# Middleware to extract and validate API Key
class ApiKeyMiddleware(BaseHTTPMiddleware):
    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        api_key = request.headers.get("X-AGENTPAY-API-KEY")

        # Validate User API key if present (recommended placement)
        if api_key:
            try:
                validation_result = agentpay_client.validate_api_key(api_key=api_key)
                if not validation_result.is_valid:
                    return JSONResponse(
                        {"error": "Unauthorized", "message": f"Invalid API Key: {validation_result.invalid_reason}"},
                        status_code=401
                    )
            except Exception as e:
                return JSONResponse(
                    {"error": "Internal Server Error", "message": "API Key validation failed"},
                    status_code=500
                )

        # Set context var for use in route handlers
        token = api_key_context.set(api_key)
        try:
            response = await call_next(request)
        finally:
            api_key_context.reset(token)
        return response

# Example MCP tool using FastMCP decorator
@mcp.tool()
async def example_tool(param1: str) -> str:
    """Example tool that demonstrates API key usage.
    
    Args:
        param1: Example parameter
    """
    # Get API key from context
    user_api_key = api_key_context.get()
    
    if user_api_key is None:
        return "Error: API Key missing"
    
    # Now you have the user_api_key (which was validated earlier by our middleware), you can use it to consume usage.
    # For example:
    # COST_CENTS = 10
    # usage_id = str(uuid.uuid4())
    # consume_result = agentpay_client.consume(
    #     api_key=user_api_key,
    #     amount_cents=COST_CENTS,
    #     usage_event_id=usage_id
    # )
    # if not consume_result.success:
    #     return f"Error: {consume_result.error_message}"
    
    return f"Tool executed with parameter: {param1}"
```

## When to Use `validate_api_key()` vs. `consume()`

* **`consume()`**: Use this when you are performing a billable action. It implicitly validates the key as part of the consumption process. If the key is invalid, `consume()` will fail.
* **`validate_api_key()`**: Use this for pre-checks if needed:
  * If you want to provide a distinct user experience for invalid keys versus insufficient funds (though `consume()` also returns error codes for this).
  * Before performing non-billable but sensitive or resource-intensive setup operations.
  * **Avoid excessive calls:** Don't call `validate_api_key()` and then immediately `consume()` for every request if not strictly necessary, as this involves two separate calls to the AgentPay system. Rely on `consume()` for validation if an operation is definitely billable.

## Next Steps

* Learn how to [Extract User API Keys](./extracting-api-keys) from requests.
* Learn how to [Charge for Usage](./charging-for-usage) of your tools.
