Fetch Customer

The Fetch Customer endpoint retrieves detailed information about a specific customer using their unique identifier. This endpoint is essential for customer profile displays, transaction processing workflows, and customer service operations. It provides complete customer data with associated application information and automatic audit logging.

Endpoint DetailsCopied!

  • Method: GET

  • URL: /api/v0/customers/{id}

  • Content Type: Not applicable (GET request)

  • Authentication: Required (x-client-key and x-client-secret)

  • Rate Limiting: Subject to standard API rate limits

Path ParametersCopied!

Parameter

Type

Required

Description

Constraints

id

string

Unique customer identifier (UUID)

Must be valid UUID format

Request HeadersCopied!

x-client-key: your_client_key_here
x-client-secret: your_client_secret_here

Request ExampleCopied!

GET /api/v0/customers/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret

Success ResponseCopied!

Status Code: 200 OK

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "John",
  "last_name": "Doe",
  "email": "john.doe@example.com",
  "phone_number": "+1-555-123-4567",
  "customer_type": "Individual",
  "status": "ACTIVE",
  "last_spent": 250.75,
  "last_purchase_date": "2024-01-10T15:30:00Z",
  "appId": "app_123456789",
  "app_id": "app_123456789",
  "createdAt": "2024-01-01T10:00:00Z",
  "updatedAt": "2024-01-15T14:30:00Z",
  "app": {
    "id": "app_123456789",
    "app_name": "My Application"
  }
}

Response Field Descriptions

Field

Type

Description

Nullable

id

string

Customer's unique identifier (UUID)

first_name

string

Customer's first name

last_name

string

Customer's last name

email

string

Customer's email address

phone_number

string

Customer's phone number with country code

customer_type

string

Account type (Individual, Business, Enterprise, Non-Profit)

status

string

Account status (ACTIVE, BLACKLISTED, DEACTIVATED)

last_spent

number

Amount of most recent transaction

last_purchase_date

string

ISO 8601 timestamp of last purchase

appId

string

Associated application identifier

app_id

string

Duplicate field for backward compatibility

createdAt

string

ISO 8601 timestamp when customer was created

updatedAt

string

ISO 8601 timestamp when customer was last updated

app

object

Associated application details

Customer Status Values

  • "ACTIVE" - Customer can access all services

  • "BLACKLISTED" - Customer is blocked from services

  • "DEACTIVATED" - Customer account is deactivated

Customer Type Values

  • "Individual" - Personal customer account

  • "Business" - Business customer account

  • "Enterprise" - Enterprise-level customer

  • "Non-Profit" - Non-profit organization

Error ResponsesCopied!

Customer Not Found (404)

{
  "statusCode": 404,
  "message": "Customer not found",
  "error": "Not Found"
}

Invalid UUID Format (400)

{
  "statusCode": 400,
  "message": "Invalid UUID format",
  "error": "Bad Request"
}

Authentication Error (401)

{
  "statusCode": 401,
  "message": "Invalid API credentials",
  "error": "Unauthorized"
}

Access Denied (403)

{
  "statusCode": 403,
  "message": "Customer does not belong to your application",
  "error": "Forbidden"
}

Rate Limit Exceeded (429)

{
  "statusCode": 429,
  "message": "Too many requests",
  "error": "Too Many Requests",
  "retry_after": 60
}

Server Error (500)

{
  "statusCode": 500,
  "message": "Internal server error",
  "error": "Internal Server Error"
}

Integration ExamplesCopied!

cURL

curl -X GET https://api.devdraft.com/api/v0/customers/550e8400-e29b-41d4-a716-446655440000 \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"

JavaScript/Node.js

Basic Implementation
async function fetchCustomer(customerId) {
  const response = await fetch(`https://api.devdraft.com/api/v0/customers/${customerId}`, {
    headers: {
      'x-client-key': process.env.DEVDRAFT_CLIENT_KEY,
      'x-client-secret': process.env.DEVDRAFT_CLIENT_SECRET
    }
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  return await response.json();
}

// Usage
try {
  const customer = await fetchCustomer('550e8400-e29b-41d4-a716-446655440000');
  console.log(`Customer: ${customer.first_name} ${customer.last_name}`);
} catch (error) {
  console.error('Failed to fetch customer:', error.message);
}
With Error Handling
async function getCustomerSafely(customerId) {
  try {
    const response = await fetch(`https://api.devdraft.com/api/v0/customers/${customerId}`, {
      headers: {
        'x-client-key': process.env.DEVDRAFT_CLIENT_KEY,
        'x-client-secret': process.env.DEVDRAFT_CLIENT_SECRET
      }
    });

    if (response.status === 404) {
      return null; // Customer not found
    }

    if (response.status === 403) {
      throw new Error('Access denied: Customer belongs to different application');
    }

    if (!response.ok) {
      throw new Error(`API Error: ${response.status} ${response.statusText}`);
    }

    return await response.json();
  } catch (error) {
    console.error(`Error fetching customer ${customerId}:`, error.message);
    throw error;
  }
}

// Usage with null checking
const customer = await getCustomerSafely('550e8400-e29b-41d4-a716-446655440000');
if (customer) {
  console.log('Customer found:', customer.email);
} else {
  console.log('Customer not found');
}
Customer Service Helper
class CustomerService {
  constructor(clientKey, clientSecret) {
    this.baseUrl = 'https://api.devdraft.com/api/v0/customers';
    this.headers = {
      'x-client-key': clientKey,
      'x-client-secret': clientSecret
    };
  }

  async getCustomer(id) {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      headers: this.headers
    });

    if (!response.ok) {
      const error = await response.json().catch(() => ({}));
      throw new Error(error.message || `HTTP ${response.status}`);
    }

    return await response.json();
  }

  async getCustomerDisplay(id) {
    const customer = await this.getCustomer(id);
    return {
      id: customer.id,
      name: `${customer.first_name} ${customer.last_name}`,
      email: customer.email,
      phone: customer.phone_number,
      status: customer.status,
      type: customer.customer_type,
      lastSpent: customer.last_spent,
      joinDate: new Date(customer.createdAt).toLocaleDateString()
    };
  }

  async isCustomerActive(id) {
    try {
      const customer = await this.getCustomer(id);
      return customer.status === 'ACTIVE';
    } catch (error) {
      if (error.message.includes('404')) {
        return false;
      }
      throw error;
    }
  }
}

// Usage
const customerService = new CustomerService(
  process.env.DEVDRAFT_CLIENT_KEY,
  process.env.DEVDRAFT_CLIENT_SECRET
);

const displayData = await customerService.getCustomerDisplay('550e8400-e29b-41d4-a716-446655440000');
console.log('Customer Display:', displayData);

Python

Basic Implementation
import requests
import os
from typing import Optional, Dict

class DevdraftCustomer:
    def __init__(self):
        self.base_url = "https://api.devdraft.com/api/v0/customers"
        self.headers = {
            'x-client-key': os.getenv('DEVDRAFT_CLIENT_KEY'),
            'x-client-secret': os.getenv('DEVDRAFT_CLIENT_SECRET')
        }

    def get_customer(self, customer_id: str) -> Optional[Dict]:
        """Fetch a customer by ID."""
        try:
            response = requests.get(
                f"{self.base_url}/{customer_id}",
                headers=self.headers
            )
            
            if response.status_code == 404:
                return None
                
            response.raise_for_status()
            return response.json()
            
        except requests.exceptions.RequestException as e:
            print(f"Error fetching customer {customer_id}: {e}")
            raise

    def get_customer_summary(self, customer_id: str) -> Optional[Dict]:
        """Get customer with formatted summary data."""
        customer = self.get_customer(customer_id)
        if not customer:
            return None
            
        return {
            'id': customer['id'],
            'full_name': f"{customer['first_name']} {customer['last_name']}",
            'contact': {
                'email': customer.get('email'),
                'phone': customer['phone_number']
            },
            'account': {
                'type': customer.get('customer_type', 'Individual'),
                'status': customer['status'],
                'created': customer['createdAt'],
                'last_updated': customer['updatedAt']
            },
            'activity': {
                'last_spent': customer['last_spent'],
                'last_purchase': customer.get('last_purchase_date')
            }
        }

    def verify_customer_access(self, customer_id: str) -> bool:
        """Verify if customer exists and is accessible."""
        try:
            customer = self.get_customer(customer_id)
            return customer is not None
        except Exception:
            return False

# Usage examples
client = DevdraftCustomer()

# Basic fetch
customer = client.get_customer('550e8400-e29b-41d4-a716-446655440000')
if customer:
    print(f"Found customer: {customer['first_name']} {customer['last_name']}")

# Get formatted summary
summary = client.get_customer_summary('550e8400-e29b-41d4-a716-446655440000')
if summary:
    print(f"Customer: {summary['full_name']}")
    print(f"Status: {summary['account']['status']}")
    print(f"Last spent: ${summary['activity']['last_spent']}")
Advanced Error Handling
import requests
from typing import Optional, Dict, Union
from dataclasses import dataclass
from datetime import datetime

@dataclass
class CustomerError:
    code: int
    message: str
    customer_id: str

class CustomerAPI:
    def __init__(self, client_key: str, client_secret: str):
        self.base_url = "https://api.devdraft.com/api/v0/customers"
        self.headers = {
            'x-client-key': client_key,
            'x-client-secret': client_secret
        }

    def get_customer(self, customer_id: str) -> Union[Dict, CustomerError]:
        """
        Fetch customer with comprehensive error handling.
        Returns customer dict on success, CustomerError on failure.
        """
        try:
            response = requests.get(
                f"{self.base_url}/{customer_id}",
                headers=self.headers,
                timeout=10
            )
            
            if response.status_code == 404:
                return CustomerError(404, "Customer not found", customer_id)
            elif response.status_code == 403:
                return CustomerError(403, "Access denied", customer_id)
            elif response.status_code == 401:
                return CustomerError(401, "Invalid credentials", customer_id)
            elif response.status_code == 429:
                return CustomerError(429, "Rate limit exceeded", customer_id)
            
            response.raise_for_status()
            return response.json()
            
        except requests.exceptions.Timeout:
            return CustomerError(408, "Request timeout", customer_id)
        except requests.exceptions.ConnectionError:
            return CustomerError(503, "Connection error", customer_id)
        except requests.exceptions.RequestException as e:
            return CustomerError(500, f"Request failed: {str(e)}", customer_id)

    def get_customer_safe(self, customer_id: str) -> Optional[Dict]:
        """Get customer, return None on any error."""
        result = self.get_customer(customer_id)
        return result if isinstance(result, dict) else None

# Usage with error handling
api = CustomerAPI(
    os.getenv('DEVDRAFT_CLIENT_KEY'),
    os.getenv('DEVDRAFT_CLIENT_SECRET')
)

result = api.get_customer('550e8400-e29b-41d4-a716-446655440000')

if isinstance(result, CustomerError):
    print(f"Error {result.code}: {result.message}")
else:
    print(f"Customer: {result['first_name']} {result['last_name']}")

PHP

Basic Implementation
<?php
class DevdraftCustomer {
    private $baseUrl = 'https://api.devdraft.com/api/v0/customers';
    private $headers;

    public function __construct($clientKey, $clientSecret) {
        $this->headers = [
            'x-client-key: ' . $clientKey,
            'x-client-secret: ' . $clientSecret
        ];
    }

    public function getCustomer($customerId) {
        $url = $this->baseUrl . '/' . $customerId;
        
        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => implode("\r\n", $this->headers),
                'timeout' => 10
            ]
        ]);

        $result = @file_get_contents($url, false, $context);
        
        if ($result === false) {
            // Check if it was a 404
            if (isset($http_response_header)) {
                foreach ($http_response_header as $header) {
                    if (strpos($header, '404') !== false) {
                        return null; // Customer not found
                    }
                }
            }
            throw new Exception('Failed to fetch customer');
        }

        return json_decode($result, true);
    }

    public function getCustomerSafe($customerId) {
        try {
            return $this->getCustomer($customerId);
        } catch (Exception $e) {
            error_log("Failed to fetch customer $customerId: " . $e->getMessage());
            return null;
        }
    }

    public function formatCustomerDisplay($customerId) {
        $customer = $this->getCustomer($customerId);
        
        if (!$customer) {
            return null;
        }

        return [
            'id' => $customer['id'],
            'name' => $customer['first_name'] . ' ' . $customer['last_name'],
            'email' => $customer['email'] ?? 'N/A',
            'phone' => $customer['phone_number'],
            'status' => $customer['status'],
            'type' => $customer['customer_type'] ?? 'Individual',
            'last_spent' => '$' . number_format($customer['last_spent'], 2),
            'join_date' => date('M j, Y', strtotime($customer['createdAt']))
        ];
    }

    public function isActiveCustomer($customerId) {
        $customer = $this->getCustomerSafe($customerId);
        return $customer && $customer['status'] === 'ACTIVE';
    }
}

// Usage
$client = new DevdraftCustomer(
    $_ENV['DEVDRAFT_CLIENT_KEY'],
    $_ENV['DEVDRAFT_CLIENT_SECRET']
);

$customerId = '550e8400-e29b-41d4-a716-446655440000';

// Basic fetch
$customer = $client->getCustomer($customerId);
if ($customer) {
    echo "Customer: " . $customer['first_name'] . " " . $customer['last_name'] . "\n";
}

// Formatted display
$display = $client->formatCustomerDisplay($customerId);
if ($display) {
    echo "Display Name: " . $display['name'] . "\n";
    echo "Status: " . $display['status'] . "\n";
    echo "Last Spent: " . $display['last_spent'] . "\n";
}

// Check if active
if ($client->isActiveCustomer($customerId)) {
    echo "Customer is active\n";
}
?>

Advanced Use CasesCopied!

Customer Profile Component

// React component for customer profile
import { useState, useEffect } from 'react';

function CustomerProfile({ customerId }) {
  const [customer, setCustomer] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function loadCustomer() {
      try {
        setLoading(true);
        const response = await fetch(`/api/customers/${customerId}`);
        
        if (response.status === 404) {
          setError('Customer not found');
          return;
        }
        
        if (!response.ok) {
          throw new Error('Failed to load customer');
        }
        
        const data = await response.json();
        setCustomer(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    if (customerId) {
      loadCustomer();
    }
  }, [customerId]);

  if (loading) return <div>Loading customer...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!customer) return <div>Customer not found</div>;

  return (
    <div className="customer-profile">
      <h2>{customer.first_name} {customer.last_name}</h2>
      <div className="customer-details">
        <p><strong>Email:</strong> {customer.email || 'Not provided'}</p>
        <p><strong>Phone:</strong> {customer.phone_number}</p>
        <p><strong>Type:</strong> {customer.customer_type || 'Individual'}</p>
        <p><strong>Status:</strong> 
          <span className={`status-${customer.status.toLowerCase()}`}>
            {customer.status}
          </span>
        </p>
        <p><strong>Last Purchase:</strong> 
          ${customer.last_spent.toFixed(2)} on{' '}
          {customer.last_purchase_date 
            ? new Date(customer.last_purchase_date).toLocaleDateString()
            : 'Never'
          }
        </p>
        <p><strong>Member since:</strong> 
          {new Date(customer.createdAt).toLocaleDateString()}
        </p>
      </div>
    </div>
  );
}

Customer Validation Service

from typing import Dict, List, Optional
import re

class CustomerValidator:
    def __init__(self, api_client):
        self.api = api_client
        
    def validate_customer_for_transaction(self, customer_id: str, amount: float) -> Dict:
        """Validate customer eligibility for a transaction."""
        customer = self.api.get_customer(customer_id)
        
        if not customer:
            return {
                'valid': False,
                'reason': 'Customer not found',
                'code': 'CUSTOMER_NOT_FOUND'
            }
        
        if customer['status'] != 'ACTIVE':
            return {
                'valid': False,
                'reason': f"Customer status is {customer['status']}",
                'code': 'CUSTOMER_INACTIVE'
            }
        
        # Business logic validations
        if customer['status'] == 'BLACKLISTED':
            return {
                'valid': False,
                'reason': 'Customer is blacklisted',
                'code': 'CUSTOMER_BLACKLISTED'
            }
        
        # Additional validations based on amount
        if amount > 10000 and not customer.get('email'):
            return {
                'valid': False,
                'reason': 'Email required for high-value transactions',
                'code': 'EMAIL_REQUIRED'
            }
        
        return {
            'valid': True,
            'customer': customer,
            'code': 'VALID'
        }

    def get_customer_risk_profile(self, customer_id: str) -> Dict:
        """Assess customer risk profile."""
        customer = self.api.get_customer(customer_id)
        
        if not customer:
            return {'risk_level': 'UNKNOWN', 'reason': 'Customer not found'}
        
        risk_factors = []
        risk_score = 0
        
        # Check account age
        from datetime import datetime
        created = datetime.fromisoformat(customer['createdAt'].replace('Z', '+00:00'))
        days_old = (datetime.now().replace(tzinfo=created.tzinfo) - created).days
        
        if days_old < 30:
            risk_factors.append('New account (less than 30 days)')
            risk_score += 2
        
        # Check transaction history
        if customer['last_spent'] == 0:
            risk_factors.append('No transaction history')
            risk_score += 3
        elif customer['last_spent'] > 5000:
            risk_factors.append('High-value transaction history')
            risk_score += 1
        
        # Check contact information
        if not customer.get('email'):
            risk_factors.append('No email address')
            risk_score += 1
        
        # Determine risk level
        if risk_score >= 5:
            risk_level = 'HIGH'
        elif risk_score >= 3:
            risk_level = 'MEDIUM'
        else:
            risk_level = 'LOW'
        
        return {
            'risk_level': risk_level,
            'risk_score': risk_score,
            'risk_factors': risk_factors,
            'customer_id': customer_id
        }

# Usage
validator = CustomerValidator(api_client)

# Validate for transaction
validation = validator.validate_customer_for_transaction(
    '550e8400-e29b-41d4-a716-446655440000', 
    1500.00
)

if validation['valid']:
    print("Customer approved for transaction")
else:
    print(f"Transaction denied: {validation['reason']}")

# Get risk profile
risk = validator.get_customer_risk_profile('550e8400-e29b-41d4-a716-446655440000')
print(f"Customer risk level: {risk['risk_level']}")

Performance ConsiderationsCopied!

Response Times

  • Average response time: 50-150ms

  • 99th percentile: <300ms

  • Timeout recommendation: 10 seconds

Caching Strategy

class CustomerCache {
  constructor(ttl = 300000) { // 5 minutes default
    this.cache = new Map();
    this.ttl = ttl;
  }

  async getCustomer(id, fetchFunction) {
    if (this.cache.has(id)) {
      const cached = this.cache.get(id);
      if (Date.now() < cached.expiry) {
        return cached.data;
      }
      this.cache.delete(id);
    }

    const customer = await fetchFunction(id);
    
    if (customer) {
      this.cache.set(id, {
        data: customer,
        expiry: Date.now() + this.ttl
      });
    }

    return customer;
  }

  invalidate(id) {
    this.cache.delete(id);
  }

  clear() {
    this.cache.clear();
  }
}

// Usage
const cache = new CustomerCache();
const customer = await cache.getCustomer(customerId, fetchCustomer);

Security ConsiderationsCopied!

Data Access Control

  • Customers are automatically scoped to your application

  • Cannot access customers from other applications

  • All requests require valid API credentials

Audit Logging

  • Every customer fetch is automatically logged for compliance

  • Includes user information, timestamp, and request metadata

  • Access patterns monitored for security anomalies

Privacy Protection

  • Sensitive data encrypted at rest and in transit

  • Compliant with GDPR, CCPA, and other privacy regulations

  • Audit trails maintained for data access

Best PracticesCopied!

1. Error Handling

Always implement comprehensive error handling for different scenarios:

async function safeGetCustomer(id) {
  try {
    const customer = await fetchCustomer(id);
    return { success: true, data: customer };
  } catch (error) {
    if (error.message.includes('404')) {
      return { success: false, error: 'CUSTOMER_NOT_FOUND' };
    }
    if (error.message.includes('403')) {
      return { success: false, error: 'ACCESS_DENIED' };
    }
    return { success: false, error: 'UNKNOWN_ERROR', details: error.message };
  }
}

2. Input Validation

Validate UUID format before making API calls:

function isValidUUID(str) {
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return uuidRegex.test(str);
}

3. Caching Strategy

Cache customer data appropriately based on your use case:

  • Profile pages: 5-15 minutes

  • Transaction processing: 1-5 minutes

  • Admin dashboards: 30 seconds to 2 minutes

4. Rate Limiting

Implement client-side rate limiting to avoid API limits:

class RateLimitedAPI {
  constructor(maxRequests = 100, windowMs = 3600000) {
    this.requests = [];
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
  }

  async makeRequest(requestFn) {
    const now = Date.now();
    this.requests = this.requests.filter(time => now - time < this.windowMs);
    
    if (this.requests.length >= this.maxRequests) {
      throw new Error('Rate limit exceeded');
    }
    
    this.requests.push(now);
    return await requestFn();
  }
}
  • GET /api/v0/customers - List customers with filtering and pagination

  • POST /api/v0/customers - Create new customers

  • PATCH /api/v0/customers/{id} - Update customer information

  • POST /api/v0/customers/{id}/liquidation_addresses - Manage customer liquidation addresses

SupportCopied!

For technical support or questions about fetching customers:

  • Ensure customer ID is a valid UUID format

  • Verify the customer belongs to your application

  • Check your API credentials and permissions

  • Contact support with the specific customer ID and error details for faster assistance