👤 Creating a Customer
You should create a Customer in Credibill when a user signs up for your application. This endpoint is used to register a new customer in the system. It is authenticated via an API Key passed as a Bearer Token in the Authorization header.- Endpoint:
https://giant-goldfish-922.convex.site/api/v1/customers/create - Method:
POST - Authentication: Authorization:
Bearer <CREDIBILL_API_KEY>
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | The customer’s primary email address (must be unique). | |
| first_name | string | No | The customer’s first name. |
| last_name | string | No | The customer’s last name. |
| phone | string | No | The customer’s phone number. |
| externalCustomerId | string | No | An ID from your internal system. |
| metadata | object | No | Arbitrary JSON data. |
Example (React + TS)
- customers.ts
- useCreateCustomer.ts
- React Component
- cURL
Copy
// src/api/customers.ts
// --- Data Types for Customer Creation ---
export type CustomerData = {
email: string;
first_name?: string;
last_name?: string;
phone?: string;
externalCustomerId?: string;
metadata?: Record<string, any>;
type?: string;
status?: string;
};
export type CustomerCreationResponse = {
success: boolean,
customerId: string,
};
// Replace with your actual Convex URL
const CONVEX_URL = 'https://YOUR_CONVEX_URL.convex.site';
const ENDPOINT = '/api/v1/customers/create';
/\*\*
- Handles the HTTP POST request to create a new customer.
- @param data - The customer payload.
- @param apiKey - The Credibill API Key for Bearer authentication.
- @returns A promise resolving to the API response data.
\*/
export async function createCustomer(
data: CustomerData,
apiKey: string
): Promise<CustomerCreationResponse> {
// 1. Validate API Key presence
if (!apiKey) {
throw new Error("API Key is required for authentication.");
}
// 2. Fetch API Call
const response = await fetch(`${CREDIBILL_URL}${ENDPOINT}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Mandatory authentication header
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify(data),
});
// 3. Handle API Response Errors
if (!response.ok) {
const errorBody = await response.json();
// Throw error to be caught by React Query's useMutation onError
throw new Error(errorBody.error || `HTTP error! Status: ${response.status}`);
}
// 4. Return successful response body
return response.json();
}
Copy
// src/hooks/useCreateCustomer.ts
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
import { createCustomer, CustomerData, CustomerCreationResponse } from '../api/customers';
// NOTE: In a real application, the API key should be retrieved securely,
// e.g., from an authentication context or securely fetched environment variable.
const useApiKey = () => {
// !! REPLACE THIS PLACEHOLDER !!
const apiKey = 'YOUR_CREDIBILL_API_KEY';
return apiKey;
};
// Define the arguments and return type for the useMutation hook
type CreateCustomerOptions = Omit<UseMutationOptions<
CustomerCreationResponse,
Error,
CustomerData
>, 'mutationFn'>;
/**
* Custom React Query hook for creating a customer.
* Uses the useMutation hook to manage the POST request state.
* @param options - Optional configuration options for useMutation (e.g., onSuccess, onError).
* @returns The mutation object provided by TanStack Query.
*/
export const useCreateCustomer = (
options?: CreateCustomerOptions
) => {
const apiKey = useApiKey();
return useMutation<CustomerCreationResponse, Error, CustomerData>({
// mutationFn takes the customer data and calls the authenticated API function
mutationFn: (data: CustomerData) => createCustomer(data, apiKey),
// Pass through any custom options (like onSuccess/onError)
...options,
});
};
Copy
import React, { useState } from 'react';
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
// --- 1. API Data Types ---
type CustomerData = {
email: string;
first_name?: string;
last_name?: string;
phone?: string;
externalCustomerId?: string;
metadata?: Record<string, any>;
type?: string;
status?: string;
};
type CustomerCreationResponse = {
success: boolean;
customerId: string;
};
// --- 2. API Fetch Function ---
const CREDIBILL_URL = 'https://giant-goldfish-922.convex.site';
const ENDPOINT = '/api/v1/customers/create';
async function createCustomer(
data: CustomerData,
apiKey: string
): Promise<CustomerCreationResponse> {
if (!apiKey) {
throw new Error("API Key is required for authentication.");
}
const response = await fetch(`${CREDIBILL_URL}${ENDPOINT}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorBody = await response.json();
throw new Error(errorBody.error || `HTTP error! Status: ${response.status}`);
}
return response.json();
}
// --- 3. Custom React Query Hook ---
const useApiKey = () => {
const apiKey = 'YOUR_CREDIBILL_API_KEY';
return apiKey;
};
type CreateCustomerOptions = Omit<UseMutationOptions<
CustomerCreationResponse,
Error,
CustomerData
> , 'mutationFn'>;
const useCreateCustomer = (
options?: CreateCustomerOptions
) => {
const apiKey = useApiKey();
return useMutation<CustomerCreationResponse, Error, CustomerData>({
mutationFn: (data: CustomerData) => createCustomer(data, apiKey),
...options,
});
};
// --- 4. Component Definition ---
const CreateCustomerForm = () => {
const [email, setEmail] = useState('');
const [firstName, setFirstName] = useState('');
const [externalId, setExternalId] = useState('');
const { mutate, isPending, isError, isSuccess, data, error, reset } = useCreateCustomer({
onSuccess: (responseData) => {
console.log(`Success! Customer ID: ${responseData.customerId}`);
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (isPending) return;
reset();
mutate({
email: email.trim(),
first_name: firstName.trim(),
externalCustomerId: externalId.trim() || undefined,
});
};
return (
<div className="p-6 max-w-lg mx-auto bg-white rounded-xl shadow-lg space-y-4">
<h2 className="text-2xl font-bold text-gray-800">New Customer Registration</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">Email (Required)</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={isPending}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label htmlFor="firstName" className="block text-sm font-medium text-gray-700">First Name</label>
<input
id="firstName"
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
disabled={isPending}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm"
/>
</div>
<div>
<label htmlFor="externalId" className="block text-sm font-medium text-gray-700">External ID (Optional)</label>
<input
id="externalId"
type="text"
value={externalId}
onChange={(e) => setExternalId(e.target.value)}
disabled={isPending}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm"
/>
</div>
<button
type="submit"
disabled={isPending}
className="w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-150 ease-in-out disabled:opacity-50"
>
{isPending ? 'Processing...' : 'Create Customer'}
</button>
</form>
{isSuccess && (
<div className="mt-4 p-3 bg-green-100 border border-green-400 text-green-700 rounded-lg">
Success! New Customer ID: <span className="font-mono">{data?.customerId}</span>
</div>
)}
{isError && (
<div className="mt-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded-lg">
Error: {error?.message}
</div>
)}
</div>
);
};
export default CreateCustomerForm;
Copy
curl -X POST 'https://giant-goldfish-922.convex.site/api/v1/customers/create' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer $YOUR_CREDIBILL_API_KEY' \
-d '{
"email": "[email protected]",
"first_name": "Jane",
"externalCustomerId": "app-user-12345"
}'
Response
When you create a customer, the API responds with a JSON object containing the success status and the unique customer ID.Copy
{
"success": true,
"customerId": "j97bp2fjzhbx59ayye52ag2gsd7ydw1c"
}
Getting Customer Details
Retrieve customer information by listing all customers for your app or fetching a specific customer by ID.- Endpoint:
https://giant-goldfish-922.convex.site/api/customers - Method:
GET - Authentication: Authorization:
Bearer <CREDIBILL_API_KEY>
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| customerId | string | No | The unique ID of the customer. If omitted, lists all customers. |
Examples
- TS
- cURL
Copy
// src/hooks/useGetCustomers.ts
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
export type Customer = {
_id: string,
email: string,
first_name?: string,
last_name?: string,
phone?: string,
externalCustomerId?: string,
metadata?: Record<string, any>,
[key: string]: any,
};
export type GetCustomersResponse = {
customers?: Customer[],
customer?: Customer,
};
const useApiKey = () => {
// !! REPLACE THIS PLACEHOLDER !!
const apiKey = 'YOUR_CREDIBILL_API_KEY';
return apiKey;
};
const CREDIBILL_URL = 'https://giant-goldfish-922.convex.site';
const ENDPOINT = '/api/customers';
type UseGetCustomersOptions = Omit<UseQueryOptions<
GetCustomersResponse,
Error
> , 'queryFn' | 'queryKey'>;
/\*\*
- Custom React Query hook for fetching customer(s)
- @param customerId - (Optional) Fetch a specific customer by ID. If omitted, fetches all customers
- @param options - Optional configuration options for useQuery
- @returns The query object provided by TanStack Query
\*/
export const useGetCustomers = (
customerId?: string,
options?: UseGetCustomersOptions
) => {
const apiKey = useApiKey();
return useQuery<GetCustomersResponse, Error>({
queryKey: ['customers', customerId],
queryFn: async () => {
if (!apiKey) {
throw new Error("API Key is required for authentication.");
}
const url = new URL(`${CREDIBILL_URL}${ENDPOINT}`);
if (customerId) {
url.searchParams.append('customerId', customerId);
}
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
});
if (!response.ok) {
const errorBody = await response.json();
throw new Error(errorBody.error || `HTTP error! Status: ${response.status}`);
}
return response.json();
},
...options,
});
};
Copy
# Get a single customer by ID
curl -X GET 'https://giant-goldfish-922.convex.site/api/customers?customerId=j97bp2fjzhbx59ayye52ag2gsd7ydw1c' \
-H 'Authorization: Bearer $YOUR_CREDIBILL_API_KEY'
# List all customers in the app
curl -X GET 'https://giant-goldfish-922.convex.site/api/customers' \
-H 'Authorization: Bearer $YOUR_CREDIBILL_API_KEY'
Response Examples
Get Single Customer:Copy
{
"customer": {
"_id": "j97bp2fjzhbx59ayye52ag2gsd7ydw1c",
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe",
"phone": "+1234567890",
"externalCustomerId": "app-user-12345",
"metadata": {
"plan": "premium",
"signup_date": "2024-01-01"
}
}
}
Copy
{
"customers": [
{
"_id": "j97bp2fjzhbx59ayye52ag2gsd7ydw1c",
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe",
"metadata": {}
},
{
"_id": "k89cq3gkzhiys60bzuf53bh3hte8zx2d",
"email": "[email protected]",
"first_name": "John",
"last_name": "Smith",
"metadata": {}
}
]
}