Skip to content

Clients API

The Clients API provides full CRUD operations for managing clients, along with user management endpoints for inviting and removing client users. You can create, read, update, and delete clients, manage custom fields, and control client portal access through these endpoints.

Get a paginated list of clients with optional search and sorting.

GET /api/v1/clients

Query Parameters:

  • search (string) - Search clients by company name
  • sortBy (string) - Sort by field: company_name, website, contact_person_name, contact_email, contact_phone (default: company_name)
  • sortDirection (string) - Sort direction: asc or desc (default: asc)
  • per_page (integer) - Results per page, max 100 (default: 15)

Response:

{
"success": true,
"data": [
{
"id": 1,
"company_name": "Acme Corporation",
"website": "https://acme.com",
"address": "123 Main St",
"city": "New York",
"country": "US",
"contact_person_name": "Jane Smith",
"contact_person_position": "CTO",
"contact_phone": "+1-555-0123",
"contact_email": "jane@acme.com",
"color": "#3B82F6",
"industry_id": 2,
"permissions": {
"can_view": true,
"can_create": true,
"can_edit_and_delete": true
},
"created_at": "2024-02-15T10:30:00.000000Z",
"updated_at": "2024-03-10T14:20:00.000000Z"
}
],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 73,
"last_page": 5
}
}

Get detailed information about a specific client, including users, custom fields, and project count.

GET /api/v1/clients/{id}

Response:

{
"success": true,
"data": {
"id": 1,
"company_name": "Acme Corporation",
"website": "https://acme.com",
"address": "123 Main St",
"city": "New York",
"country": "US",
"contact_person_name": "Jane Smith",
"contact_person_position": "CTO",
"contact_phone": "+1-555-0123",
"contact_email": "jane@acme.com",
"color": "#3B82F6",
"industry_id": 2,
"access_status": "active",
"custom_fields": {
"contract_type": "Annual",
"sla_tier": "Premium"
},
"projects_count": 5,
"users": [
{
"id": 10,
"name": "Jane Smith",
"email": "jane@acme.com",
"type": "client",
"created_at": "2024-02-15T10:30:00.000000Z"
}
],
"permissions": {
"can_view": true,
"can_create": true,
"can_edit_and_delete": true
},
"created_at": "2024-02-15T10:30:00.000000Z",
"updated_at": "2024-03-10T14:20:00.000000Z"
}
}

Create a new client with optional custom fields.

POST /api/v1/clients

Request Body:

{
"company_name": "Globex Corporation",
"website": "https://globex.com",
"address": "456 Corporate Blvd",
"city": "San Francisco",
"country": "US",
"industry_id": 3,
"contact_person_name": "Hank Scorpio",
"contact_person_position": "CEO",
"contact_phone": "+1-555-0456",
"contact_email": "hank@globex.com",
"color": "#10B981",
"custom_fields": {
"contract_type": "Monthly",
"sla_tier": "Standard"
}
}

Response:

{
"success": true,
"message": "Client created successfully.",
"data": {
"id": 2,
"company_name": "Globex Corporation"
}
}

Update an existing client.

PUT /api/v1/clients/{id}
PATCH /api/v1/clients/{id}

Request Body (partial update with PATCH):

{
"company_name": "Globex Corp",
"contact_email": "security@globex.com",
"custom_fields": {
"contract_type": "Annual",
"sla_tier": "Premium"
}
}

All fields from the create endpoint are accepted. With PATCH, only provided fields are updated. Custom fields are merged — existing keys not included in the request are preserved.

Response:

{
"success": true,
"message": "Client updated successfully.",
"data": {
"id": 2,
"company_name": "Globex Corp",
// ... full client details
}
}

Soft delete a client. The client cannot be deleted if it has active (non-archived) projects.

DELETE /api/v1/clients/{id}

Response:

{
"success": true,
"message": "Client deleted successfully."
}

Get all users associated with a client.

GET /api/v1/clients/{id}/users

Response:

{
"success": true,
"data": [
{
"id": 10,
"name": "Jane Smith",
"email": "jane@acme.com",
"type": "client",
"created_at": "2024-02-15T10:30:00.000000Z"
},
{
"id": 11,
"name": "John Doe",
"email": "john@acme.com",
"type": "client",
"created_at": "2024-03-01T09:00:00.000000Z"
}
]
}

Invite a new user to the client portal. The user will receive an email invitation to set up their account.

POST /api/v1/clients/{id}/users/invite

Request Body:

{
"name": "Bob Wilson",
"email": "bob@acme.com"
}

Response:

{
"success": true,
"message": "User invited successfully.",
"data": {
"id": 12,
"name": "Bob Wilson",
"email": "bob@acme.com"
}
}

Remove a user from the client. The user account will be soft-deleted.

DELETE /api/v1/clients/{id}/users/{user_id}

Response:

{
"success": true,
"message": "User deleted successfully."
}
Terminal window
curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"company_name": "Acme Corporation",
"website": "https://acme.com",
"contact_person_name": "Jane Smith",
"contact_email": "jane@acme.com",
"custom_fields": {
"contract_type": "Annual",
"sla_tier": "Premium"
}
}' \
https://your-instance.pentestpad.com/api/v1/clients
Terminal window
curl -H "Authorization: Bearer your_api_key" \
"https://your-instance.pentestpad.com/api/v1/clients?search=acme&sortBy=company_name&sortDirection=asc&per_page=25"
Terminal window
curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Bob Wilson",
"email": "bob@acme.com"
}' \
https://your-instance.pentestpad.com/api/v1/clients/1/users/invite
// Create a new client
const newClient = await fetch('/api/v1/clients', {
method: 'POST',
headers: {
'Authorization': 'Bearer your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
company_name: 'Acme Corporation',
website: 'https://acme.com',
contact_person_name: 'Jane Smith',
contact_email: 'jane@acme.com'
})
}).then(r => r.json());
// Get client details with users
const client = await fetch(`/api/v1/clients/${newClient.data.id}`, {
headers: { 'Authorization': 'Bearer your_api_key' }
}).then(r => r.json());
// Invite a user to the client portal
const invitation = await fetch(`/api/v1/clients/${newClient.data.id}/users/invite`, {
method: 'POST',
headers: {
'Authorization': 'Bearer your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Bob Wilson',
email: 'bob@acme.com'
})
}).then(r => r.json());
  • company_name (string, max 255)
  • website (string, valid URL, max 255)
  • address (string, max 255)
  • city (string, max 255)
  • country (string, max 255)
  • industry_id (integer, must reference a valid industry)
  • contact_person_name (string, max 255)
  • contact_person_position (string, max 255)
  • contact_phone (string, max 255)
  • contact_email (string, valid email, max 255)
  • color (string, max 7 characters, e.g. #3B82F6)
  • custom_fields (object, key-value pairs)
  • name (string) - Full name of the user
  • email (string, valid email) - Must be unique across all users
{
"success": false,
"message": "Client not found."
}
{
"success": false,
"message": "The given data was invalid.",
"errors": {
"company_name": ["The company name field is required."],
"website": ["The website field must be a valid URL."]
}
}
{
"success": false,
"message": "Cannot delete client with active projects. Please archive or reassign all projects first."
}
{
"success": false,
"message": "This user does not belong to this client."
}
{
"success": false,
"message": "The given data was invalid.",
"errors": {
"email": ["The email has already been taken."]
}
}