Complete reference for all classes, methods, and response models in the nekuda Python SDK.

NekudaClient

The main client for interacting with the nekuda API. It should be instantiated once and reused. It uses https://api.nekuda.ai as the default base_url.

Constructor

from nekuda import NekudaClient

client = NekudaClient(
api_key="sk_live_your_api_key", # Replace with your actual secret API key # base_url="https://api.nekuda.ai", # Default, usually not needed
timeout=30, # Optional: request timeout in seconds
max_retries=3, # Optional: max retries for retryable errors (429, 5xx)
backoff_factor=0.5, # Optional: backoff factor for retries
)
api_key
string
required

Your Nekuda Secret API key (starts with sk_live_ or sk_test_). Must be provided either directly or via the NEKUDA_API_KEY environment variable.

base_url
string
default:"https://api.nekuda.ai"

The base URL for the nekuda API. Defaults to production. Only change this for staging/testing (e.g., https://staging-api.nekuda.ai) or if using a mock server. The SDK normalizes this URL.

timeout
int
default:"30"

Request timeout in seconds for HTTP calls.

max_retries
int
default:"3"

Maximum number of retry attempts for retryable errors (HTTP 429, 5xx).

backoff_factor
float
default:"0.5"

Multiplier for exponential backoff between retries. sleep = (2 ** (retry_attempt - 1)) * backoff_factor.

Class Methods

NekudaClient.from_env()

Factory method to create a NekudaClient instance configured from environment variables.

from nekuda import NekudaClient

# Reads NEKUDA_API_KEY for the API key.
# Reads NEKUDA_BASE_URL (optional) for the base URL, defaults to https://api.nekuda.ai.
# Allows overriding other client parameters too.
client = NekudaClient.from_env(
    # api_key_var="CUSTOM_API_KEY_ENV_VAR_NAME", # Default: "NEKUDA_API_KEY"
    # base_url_var="CUSTOM_BASE_URL_ENV_VAR_NAME", # Default: "NEKUDA_BASE_URL"
    timeout=45 # Example: override default timeout
)

This is the recommended way to initialize the client for most applications.

Instance Methods

UserContext

A convenience wrapper returned by client.user(user_id) that automatically includes the user_id in all its API calls. This is the recommended way to interact with user-specific endpoints.

from nekuda import NekudaClient, MandateData

client = NekudaClient.from_env()
user_context = client.user("unique_user_id_789")

# Example mandate creation using UserContext
mandate_details = MandateData(
    product="Super Plan", price=29.00, currency="EUR", merchant="My Service"
)
try:
    mandate_response = user_context.create_mandate(mandate_details)
    print(f"Mandate ID from UserContext: {mandate_response.mandate_id}")

    # Request reveal token using UserContext and the new mandate_id
    reveal_token_response = user_context.request_card_reveal_token(mandate_response.mandate_id)
    print(f"Reveal Token from UserContext: {reveal_token_response.reveal_token[:10]}...")

    # Reveal card details using UserContext and the new token
    card_object = user_context.reveal_card_details(reveal_token_response.reveal_token)
    print(f"Cardholder via UserContext: {card_object.cardholder_name}")
except NekudaError as e:
    print(f"UserContext operation failed: {e}")

The UserContext object has the following methods, which mirror the client methods but without requiring user_id as a parameter:

  • create_mandate(mandate_data: MandateData, request_id: Optional[str] = None) -> MandateCreateResponse
  • request_card_reveal_token(mandate_id: Union[str, int]) -> CardRevealTokenResponse
  • reveal_card_details(reveal_token: str) -> CardDetailsResponse

Data Models

MandateData

Represents the user’s intent to purchase. This data is sent when creating a mandate. It includes built-in Pydantic validation.

from nekuda import MandateData, NekudaValidationError

try:
    mandate = MandateData(
        # Required fields
        product="Premium Subscription",     # Name of the product/service
        price=49.99,                       # Price (must be > 0)
        currency="USD",                    # 3-letter ISO currency code

        # Optional fields
        merchant="Your Company LLC",       # Your merchant name displayed to user
        merchant_link="https://yourcompany.com/premium", # Link to product/merchant
        product_description="Monthly access to all premium features and support.",
        confidence_score=0.95,             # Your system's confidence (0.0 to 1.0) that this is a legitimate user intent
        # request_id: Optional[str] = None # Auto-generated UUID if not provided for create_mandate idempotency
    )
except NekudaValidationError as e:
    print(f"Invalid MandateData: {e}")
product
string
required

Name of the product or service being purchased.

price
float
required

Price of the product. Must be a positive number.

currency
string
required

3-letter ISO currency code (e.g., “USD”, “EUR”).

merchant
string

Name of the merchant or store.

URL related to the merchant or product.

product_description
string

A more detailed description of the product or service.

confidence_score
float

A score (0.0 to 1.0) indicating your system’s confidence in this purchase intent.

request_id
Optional[str]

An optional idempotency key for the create_mandate call. If not provided, the SDK generates one.

Response Models

All nekuda SDK API methods return typed Pydantic models with automatic validation.

Response from create_mandate() or user_context.create_mandate().

class MandateCreateResponse:
    mandate_id: int                    # Unique ID for the created mandate
    request_id: str                    # Request tracking ID (idempotency key used or generated)
    customer_id: str                   # The user_id you provided for this mandate
    created_at: datetime               # Timestamp of mandate creation (UTC)
    updated_at: Optional[datetime]     # Timestamp of last update (UTC), if any

Example Usage:

# mandate_resp = user_context.create_mandate(...)
# print(f"New Mandate ID: {mandate_resp.mandate_id}")
# print(f"Associated User (Customer ID): {mandate_resp.customer_id}")

Exceptions

All exceptions raised by the nekuda SDK inherit from a base NekudaError class. Refer to the Error Handling guide for a detailed hierarchy and usage patterns.

Key Exception Classes

  • NekudaError (Base class)
  • NekudaApiError (Base for API-returned errors)
    • AuthenticationError (401 - Invalid API key)
    • InvalidRequestError (4xx - e.g., bad parameters, resource not found)
      • CardNotFoundError (Specific 404 for card not found)
    • RateLimitError (429 - Rate limit exceeded, SDK retries these)
    • ServerError (5xx - Server-side issue at Nekuda, SDK retries these)
  • NekudaConnectionError (Network issues like DNS or TCP errors)
  • NekudaValidationError (Client-side or response data validation failure against Pydantic models)

Common Exception Attributes (for NekudaApiError and its children)

message
str

Human-readable error message.

code
Optional[str]

A machine-readable error code string from the API, if available (e.g., invalid_api_key, mandate_not_found).

status_code
Optional[int]

The HTTP status code returned by the API (e.g., 401, 404, 500).

Usage Examples

This example shows the an end-to-end flow: creating a client, getting a user context, creating a mandate, requesting a reveal token, and revealing card details.

from nekuda import NekudaClient, MandateData, NekudaError
import os # For environment variables

# Best practice: Initialize client from environment variables
# Ensure NEKUDA_API_KEY is set in your environment.
# NEKUDA_BASE_URL defaults to https://api.nekuda.ai if not set.
client = NekudaClient.from_env()

# Each of your users must have a unique identifier string.
user_identifier = "example_user_e2e_flow_001"
user = client.user(user_identifier)

try:
    # Step 1: Create a mandate (intent to purchase)
    print(f"Creating mandate for user '{user_identifier}'...")
    mandate_payload = MandateData(
        product="Ultimate Gadget X",
        price=299.99,
        currency="USD",
        merchant="Future Tech Inc.",
        product_description="The latest and greatest gadget with all new features."
    )
    mandate_resp = user.create_mandate(mandate_payload)
    print(f"✅ Mandate created successfully! ID: {mandate_resp.mandate_id}")

    # Step 2: Request a card reveal token using the mandate_id from Step 1
    print(f"Requesting card reveal token for mandate ID '{mandate_resp.mandate_id}'...")
    reveal_token_resp = user.request_card_reveal_token(
        mandate_id=mandate_resp.mandate_id
    )
    print(f"🔑 Reveal token received: {reveal_token_resp.reveal_token[:15]}...")

    # Step 3: Reveal the actual card details using the token from Step 2
    print(f"Revealing card details with token...")
    card_details = user.reveal_card_details(reveal_token_resp.reveal_token)

    # Now you have the card details to use with your payment processor
    print(f"💳 Card details revealed for user '{user_identifier}':")
    print(f"   Card Number: **** **** **** {card_details.card_number[-4:]}") # Only print last 4 for security
    print(f"   Expiry Date: {card_details.card_expiry_date}")
    print(f"   Cardholder Name: {card_details.cardholder_name}")
    print("🎉 Full flow completed successfully!")

except CardNotFoundError as e:
    print(f"Card Not Found for user '{e.user_id}': {e.message}")
    print("This often means payment details were not collected or user_id mismatch.")
except InvalidRequestError as e:
    print(f"Invalid Request: {e.message} (Code: {e.code}, HTTP Status: {e.status_code})")
    print("Check your parameters, like mandate_id format or if it exists.")
except AuthenticationError as e:
    print(f"Authentication Failed: {e.message}. Check your NEKUDA_API_KEY.")
except NekudaValidationError as e:
    print(f"Data Validation Error: {e}. This could be an issue with data sent or received.")
except NekudaApiError as e:
    print(f"nekuda API Error: {e.message} (Code: {e.code}, HTTP Status: {e.status_code})")
except NekudaError as e: # Catch any other Nekuda SDK-specific errors
    print(f"A nekuda SDK error occurred: {e}")
except Exception as e: # Fallback for unexpected errors
    print(f"An unexpected error occurred: {e}")

Global Default Client Pattern

For simpler scripts or applications where dependency injection is overkill.

from nekuda import set_default_client, get_default_client, NekudaClient, MandateData

# Set once at startup (e.g., in your main script or app initialization)
set_default_client(NekudaClient.from_env()) # Configured from environment

def some_business_logic_function(user_id: str, product_details: dict):
    client_instance = get_default_client() # Retrieve the globally set client
    user_context = client_instance.user(user_id)

    mandate_info = MandateData(
        product=product_details["name"],
        price=product_details["price"],
        currency=product_details["currency"],
        merchant="Global Client Store"
    )
    try:
        mandate_response = user_context.create_mandate(mandate_info)
        # ... continue with reveal token and card details ...
        print(f"Mandate {mandate_response.mandate_id} created via global client for {user_id}")
    except NekudaError as e:
        print(f"Error in business logic with global client: {e}")

# Example call
# some_business_logic_function("user_global_client_test", {"name": "Test Product", "price": 1.00, "currency": "USD"})

While convenient, the global client pattern can make it harder to manage configurations and test components in isolation in larger applications. Use with discretion.

What’s Next?