Vydapay API Reference

Complete reference documentation for the Vydapay REST API. Everything you need to integrate card issuing and expense management.

API Version: v1 Base URL: https://api.vydapay.com/v1

Authentication

The Vydapay API uses API keys to authenticate requests. You can view and manage your API keys in your dashboard.

Include your API key in the Authorization header using Bearer token format:

Authentication Header
# Using cURL
curl https://api.vydapay.com/v1/cards \
  -H "Authorization: Bearer sk_live_abc123def456..."
Keep your keys secure. Your API keys carry many privileges. Do not share them in publicly accessible areas such as GitHub, client-side code, or public repositories.

Test vs Live Keys

Key TypePrefixPurpose
Test Secret Keysk_test_Development and testing. Transactions are simulated.
Live Secret Keysk_live_Production use. Real transactions are processed.
Publishable Keypk_Client-side use only. Limited access.

Errors

Vydapay uses conventional HTTP response codes. Codes in the 2xx range indicate success, 4xx indicate client errors, and 5xx indicate server errors.

CodeMeaningDescription
200OKRequest succeeded
201CreatedResource successfully created
400Bad RequestInvalid parameters provided
401UnauthorizedInvalid or missing API key
403ForbiddenValid key but insufficient permissions
404Not FoundResource doesn't exist
409ConflictRequest conflicts with current state
422UnprocessableValid syntax but cannot be processed
429Too Many RequestsRate limit exceeded
500Server ErrorSomething went wrong on our end

Error Response Format

Error Response
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "The spending_limit amount must be a positive integer.",
    "param": "spending_limit.amount",
    "doc_url": "https://docs.vydapay.com/errors/parameter_invalid"
  }
}

Error Types

TypeDescription
api_errorSomething went wrong on our end (rare)
authentication_errorInvalid API key or permissions issue
invalid_request_errorInvalid parameters in the request
rate_limit_errorToo many requests in a short time
card_errorCard-specific error (declined, frozen, etc.)

Handling Errors

Error Handling Example
try {
  const card = await vydapay.cards.create({
    type: 'virtual',
    currency: 'GBP',
    cardholder_id: 'ch_abc123'
  });
} catch (error) {
  if (error.type === 'invalid_request_error') {
    console.log('Invalid parameters:', error.message);
  } else if (error.type === 'authentication_error') {
    console.log('Check your API key');
  } else {
    console.log('Unexpected error:', error.message);
  }
}

Pagination

List endpoints use cursor-based pagination for efficient retrieval of large datasets.

Parameters

ParameterTypeDefaultDescription
limitinteger25Number of results (1-100)
starting_afterstringCursor for next page (use last item's ID)
ending_beforestringCursor for previous page
Paginated Response
{
  "object": "list",
  "data": [
    { "id": "card_xyz789", ... },
    { "id": "card_abc456", ... }
  ],
  "has_more": true,
  "url": "/v1/cards"
}

Iterating Through Pages

Auto-pagination Example
// Using auto-pagination (recommended)
for await (const card of vydapay.cards.list({ limit: 100 })) {
  console.log(card.id);
}

// Manual pagination
let hasMore = true;
let startingAfter = null;

while (hasMore) {
  const params = { limit: 100 };
  if (startingAfter) params.starting_after = startingAfter;
  
  const response = await vydapay.cards.list(params);
  
  for (const card of response.data) {
    console.log(card.id);
  }
  
  hasMore = response.has_more;
  if (response.data.length > 0) {
    startingAfter = response.data[response.data.length - 1].id;
  }
}

Idempotency

The API supports idempotency for safely retrying requests without accidentally performing the same operation twice. This is useful when an API call is disrupted in transit.

To perform an idempotent request, provide an Idempotency-Key header:

Idempotent Request
curl -X POST https://api.vydapay.com/v1/cards \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: unique-request-id-abc123" \
  -H "Content-Type: application/json" \
  -d '{"type": "virtual", "currency": "GBP", "cardholder_id": "ch_abc123"}'
Idempotency keys expire after 24 hours. Use UUIDs or another random string with enough entropy to avoid collisions.

Rate Limits

EnvironmentLimit
Test mode100 requests/minute
Live mode (Growth)500 requests/minute
Live mode (Business)1,000 requests/minute
Live mode (Enterprise)Custom (contact sales)

Rate limit headers are included in every response:

Rate Limit Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1701432000

Cards

Cards are the core resource. Create virtual or physical Mastercard cards, assign them to cardholders, and configure spending controls.

The Card Object

Card Object
{
  "id": "card_xyz789def456",
  "object": "card",
  "type": "virtual",
  "status": "active",
  "currency": "GBP",
  "cardholder_id": "ch_abc123",
  "last_four": "4821",
  "exp_month": 12,
  "exp_year": 2027,
  "brand": "mastercard",
  "spending_limit": {
    "amount": 50000,
    "interval": "monthly"
  },
  "spending_controls": {
    "allowed_categories": ["travel", "office_supplies"],
    "blocked_categories": ["gambling"],
    "allowed_countries": ["GB", "IE", "FR"]
  },
  "metadata": {
    "department": "engineering",
    "cost_center": "CC-1234"
  },
  "created_at": "2025-12-02T10:30:00Z",
  "updated_at": "2025-12-02T10:30:00Z"
}
POST /v1/cards

Creates a new virtual or physical card for a cardholder.

Parameters

ParameterTypeRequiredDescription
typestringYesvirtual or physical
currencystringYesISO 4217 code: GBP, EUR, USD
cardholder_idstringYesID of the cardholder
spending_limit.amountintegerNoLimit in smallest currency unit (pence)
spending_limit.intervalstringNodaily, weekly, monthly, yearly, per_transaction, all_time
spending_controlsobjectNoCategory, merchant, and geographic restrictions
metadataobjectNoUp to 20 key-value pairs
Create a Virtual Card
curl -X POST https://api.vydapay.com/v1/cards \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "type": "virtual",
    "currency": "GBP",
    "cardholder_id": "ch_abc123def456",
    "spending_limit": {
      "amount": 50000,
      "interval": "monthly"
    },
    "spending_controls": {
      "allowed_categories": ["travel", "software_services"],
      "blocked_categories": ["gambling", "adult_entertainment"],
      "allowed_countries": ["GB", "IE", "FR", "DE"]
    },
    "metadata": {
      "department": "engineering",
      "project": "Q4-infrastructure"
    }
  }'
GET /v1/cards

Returns a paginated list of all cards.

Query Parameters

ParameterDescription
cardholder_idFilter by cardholder
statusactive, frozen, cancelled
typevirtual or physical
created_afterISO 8601 timestamp
created_beforeISO 8601 timestamp
POST /v1/cards/:id/freeze

Temporarily disables a card. All transactions will be declined until the card is unfrozen.

Freeze a Card
const card = await vydapay.cards.freeze('card_xyz789def456');

console.log(card.status); // 'frozen'
PATCH /v1/cards/:id

Updates a card's spending limits, controls, or metadata.

Update Spending Limit
const card = await vydapay.cards.update('card_xyz789def456', {
  spending_limit: {
    amount: 100000, // Increase to £1,000
    interval: 'monthly'
  },
  metadata: {
    project: 'Q1-expansion' // Update metadata
  }
});

Cardholders

A cardholder represents an individual who can be assigned cards.

POST /v1/cardholders

Parameters

ParameterTypeRequiredDescription
emailstringYesEmail address
name.first_namestringYesFirst name
name.last_namestringYesLast name
phonestringNoE.164 format (+44...)
billing_addressobjectFor physicalRequired for physical card delivery
Create a Cardholder
const cardholder = await vydapay.cardholders.create({
  email: 'sarah.mitchell@company.com',
  name: {
    first_name: 'Sarah',
    last_name: 'Mitchell'
  },
  phone: '+447700900123',
  billing_address: {
    line1: '123 Business Street',
    city: 'London',
    postal_code: 'EC1A 1BB',
    country: 'GB'
  },
  metadata: {
    employee_id: 'EMP-4521',
    department: 'Sales'
  }
});

console.log(cardholder.id); // ch_abc123def456

Transactions

Transactions represent card purchases, refunds, and other activity. Created automatically when cards are used.

The Transaction Object

Transaction Object
{
  "id": "txn_abc123xyz789",
  "object": "transaction",
  "amount": 4599,
  "currency": "GBP",
  "status": "completed",
  "type": "purchase",
  "card_id": "card_xyz789def456",
  "cardholder_id": "ch_abc123def456",
  "merchant": {
    "name": "AMAZON UK",
    "category": "online_shopping",
    "category_code": "5411",
    "city": "London",
    "country": "GB"
  },
  "receipt": {
    "id": "rcpt_def456",
    "status": "matched"
  },
  "created_at": "2025-12-02T14:30:00Z"
}
GET /v1/transactions

Query Parameters

ParameterDescription
card_idFilter by card
cardholder_idFilter by cardholder
statuspending, completed, declined, refunded
typepurchase, refund, atm_withdrawal
min_amountMinimum amount in pence
max_amountMaximum amount in pence
created_afterISO 8601 timestamp
created_beforeISO 8601 timestamp
List Transactions with Filters
// Get all transactions over £100 from the last 7 days
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);

const transactions = await vydapay.transactions.list({
  min_amount: 10000, // £100 in pence
  created_after: oneWeekAgo.toISOString(),
  status: 'completed',
  limit: 50
});

for (const txn of transactions.data) {
  console.log(`£\${txn.amount / 100} at \${txn.merchant.name}`);
}

Receipts

Upload and manage receipts. Our AI automatically matches receipts to transactions.

POST /v1/transactions/:id/receipt

Attach a receipt to a transaction.

Upload Receipt
const fs = require('fs');

const receipt = await vydapay.transactions.attachReceipt(
  'txn_abc123xyz789',
  {
    file: fs.createReadStream('./receipt.pdf'),
    // Or base64 encoded
    // file_base64: 'data:image/png;base64,iVBOR...'
  }
);

console.log(receipt.status); // 'processing' or 'matched'

Spending Controls

Configure restrictions on how and where cards can be used.

Control Types

ControlDescriptionExample
allowed_categoriesOnly allow specific merchant categories["travel", "software_services"]
blocked_categoriesBlock specific merchant categories["gambling", "atm"]
allowed_countriesOnly allow transactions in specific countries["GB", "IE", "FR"]
blocked_countriesBlock transactions in specific countries["RU", "BY"]
allowed_merchantsLock card to specific merchant IDs["mid_amazon", "mid_github"]

Merchant Category Codes

Common categories you can allow or block:

airlines car_rental hotels restaurants fuel groceries office_supplies software_services advertising gambling atm adult_entertainment

Webhooks

Receive real-time notifications about events in your account.

Available Events

EventDescription
transaction.createdNew transaction authorised
transaction.completedTransaction settled
transaction.declinedTransaction declined
transaction.refundedTransaction refunded
card.createdNew card created
card.frozenCard frozen
card.unfrozenCard unfrozen
card.cancelledCard cancelled
receipt.matchedReceipt matched to transaction
spending_limit.reachedCard hit spending limit

Webhook Payload

Webhook Event
{
  "id": "evt_abc123",
  "object": "event",
  "type": "transaction.created",
  "created_at": "2025-12-02T14:30:00Z",
  "data": {
    "object": {
      "id": "txn_abc123xyz789",
      "amount": 4599,
      "currency": "GBP",
      // ... full transaction object
    }
  }
}

Verifying Webhook Signatures

Verify Signature
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSig)
  );
}

// In your webhook handler
app.post('/webhooks/vydapay', (req, res) => {
  const signature = req.headers['x-vydapay-signature'];
  
  if (!verifyWebhook(req.rawBody, signature, webhookSecret)) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = JSON.parse(req.rawBody);
  
  switch (event.type) {
    case 'transaction.created':
      handleNewTransaction(event.data.object);
      break;
    case 'card.frozen':
      notifyCardholderOfFreeze(event.data.object);
      break;
  }
  
  res.status(200).send('OK');
});

Account

Retrieve information about your Vydapay account.

GET /v1/account/balance
Response
{
  "object": "balance",
  "available": {
    "amount": 2450000,
    "currency": "GBP"
  },
  "pending": {
    "amount": 125000,
    "currency": "GBP"
  }
}