Skip to main content

Deals API

The Deals API allows you to manage sales opportunities and pipeline in your ISAO system. You can list, create, update deals and track their progress through your sales process.

📋 Table of Contents

🔐 Authentication

All deals endpoints require the deals:read or deals:write permissions:

  • Read operations: deals:read permission
  • Write operations: deals:write permission
Authorization: Bearer YOUR_API_KEY

🔗 Endpoints Overview

MethodEndpointPermissionDescription
GET/dealsdeals:readList all deals with pagination and filtering
GET/deals/:iddeals:readGet specific deal by ID
POST/dealsdeals:writeCreate new deal
PUT/deals/:id/statusdeals:writeUpdate deal status and stage

Base URL: https://staging.isao.io/api/external

📊 Deal Object Structure

{
"id": "deal_123",
"title": "Enterprise Software License",
"description": "Annual software license for 500 users",
"value": 50000.00,
"currency": "EUR",
"stage": "negotiation",
"priority": "high",
"probability": 75,
"status": "open",
"expectedCloseDate": "2024-12-15T00:00:00.000Z",
"actualCloseDate": null,
"source": "website",
"contact": {
"id": "contact_456",
"name": "John Smith",
"email": "john@techcorp.com",
"phoneNumber": "+33123456789",
"currentCompany": "Tech Corp"
},
"assignedTo": {
"id": "user_789",
"name": "Sales Rep",
"email": "sales@isao.io"
},
"createdAt": "2024-11-01T10:30:00.000Z",
"updatedAt": "2024-11-08T09:15:00.000Z"
}

Deal Status Values

  • open: Deal is active and in progress
  • won: Deal was successfully closed
  • lost: Deal was lost or abandoned

Priority Values

  • low: Low priority deal
  • medium: Medium priority deal (default)
  • high: High priority deal

📑 List Deals

Retrieve a paginated list of deals from your company.

Request

GET /deals

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number (minimum: 1)
limitinteger50Items per page (maximum: 200)
statusstring-Filter by status: open, won, lost
stagestring-Filter by deal stage
createdSincestring-ISO 8601 date, deals created after this date
updatedSincestring-ISO 8601 date, deals updated after this date

Example Request

curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://staging.isao.io/api/external/deals?status=open&stage=negotiation&limit=10"

Response

{
"success": true,
"data": [
{
"id": "deal_123",
"title": "Enterprise Software License",
"value": 50000.00,
"currency": "EUR",
"stage": "negotiation",
"priority": "high",
"probability": 75,
"status": "open",
"expectedCloseDate": "2024-12-15T00:00:00.000Z",
"actualCloseDate": null,
"description": "Annual software license for 500 users",
"source": "website",
"contact": {
"id": "contact_456",
"name": "John Smith",
"email": "john@techcorp.com",
"phoneNumber": "+33123456789",
"currentCompany": "Tech Corp"
},
"assignedTo": {
"id": "user_789",
"name": "Sales Rep",
"email": "sales@isao.io"
},
"createdAt": "2024-11-01T10:30:00.000Z",
"updatedAt": "2024-11-08T09:15:00.000Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"total": 1,
"totalPages": 1
}
}

💰 Get Deal by ID

Retrieve a specific deal by its ID.

Request

GET /deals/:id

Example Request

curl -H "Authorization: Bearer YOUR_API_KEY" \
https://staging.isao.io/api/external/deals/deal_123

Response

{
"success": true,
"data": {
"id": "deal_123",
"title": "Enterprise Software License",
"value": 50000.00,
"currency": "EUR",
"stage": "negotiation",
"priority": "high",
"probability": 75,
"status": "open",
"expectedCloseDate": "2024-12-15T00:00:00.000Z",
"actualCloseDate": null,
"description": "Annual software license for 500 users",
"source": "website",
"contact": {
"id": "contact_456",
"name": "John Smith",
"email": "john@techcorp.com",
"phoneNumber": "+33123456789",
"currentCompany": "Tech Corp"
},
"assignedTo": {
"id": "user_789",
"name": "Sales Rep",
"email": "sales@isao.io"
},
"createdAt": "2024-11-01T10:30:00.000Z",
"updatedAt": "2024-11-08T09:15:00.000Z"
}
}

➕ Create Deal

Create a new deal in your sales pipeline.

Request

POST /deals
Content-Type: application/json

Required Fields

FieldTypeDescription
titlestringDeal title (max 255 chars)
valuenumberDeal value (minimum: 0)

Optional Fields

FieldTypeDescription
descriptionstringDeal description
currencystringCurrency code (default: "EUR")
stagestringDeal stage in your pipeline
prioritystringlow, medium (default), high
probabilitynumberSuccess probability 0-100%
expectedCloseDatestringExpected close date (ISO 8601 format)
sourcestringDeal source (e.g., "website", "referral")
contactIdstringAssociated contact ID
assignedToIdstringAssigned user ID

Example Request

curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Website Redesign Project",
"description": "Complete website redesign and development",
"value": 25000,
"currency": "EUR",
"stage": "proposal",
"priority": "medium",
"probability": 60,
"expectedCloseDate": "2024-12-30",
"source": "website",
"contactId": "contact_456"
}' \
https://staging.isao.io/api/external/deals

Success Response (201 Created)

{
"success": true,
"data": {
"id": "deal_789",
"title": "Website Redesign Project",
"value": 25000,
"currency": "EUR",
"stage": "proposal",
"priority": "medium",
"probability": 60,
"status": "open",
"expectedCloseDate": "2024-12-30T00:00:00.000Z",
"description": "Complete website redesign and development",
"source": "website",
"contact": {
"id": "contact_456",
"name": "John Smith",
"email": "john@techcorp.com",
"phoneNumber": "+33123456789",
"currentCompany": "Tech Corp"
},
"assignedTo": null,
"createdAt": "2024-11-08T10:30:00.000Z",
"updatedAt": "2024-11-08T10:30:00.000Z"
}
}

Error Response (400 Bad Request)

If the associated contact doesn't exist:

{
"success": false,
"error": "Contact not found or does not belong to your company"
}

📈 Update Deal Status

Update a deal's status, stage, or close date.

Request

PUT /deals/:id/status
Content-Type: application/json

Fields

FieldTypeDescription
statusstringNew status: open, won, lost
stagestringNew stage in your pipeline
actualCloseDatestringActual close date (ISO 8601) for won/lost deals

Example Request - Mark Deal as Won

curl -X PUT \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "won",
"stage": "closed-won",
"actualCloseDate": "2024-11-08T15:30:00.000Z"
}' \
https://staging.isao.io/api/external/deals/deal_789/status

Example Request - Update Stage

curl -X PUT \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"stage": "contract-review"
}' \
https://staging.isao.io/api/external/deals/deal_789/status

Response

{
"success": true,
"data": {
"id": "deal_789",
"title": "Website Redesign Project",
"value": 25000,
"currency": "EUR",
"stage": "closed-won",
"priority": "medium",
"probability": 60,
"status": "won",
"expectedCloseDate": "2024-12-30T00:00:00.000Z",
"actualCloseDate": "2024-11-08T15:30:00.000Z",
"description": "Complete website redesign and development",
"source": "website",
"contact": {
"id": "contact_456",
"name": "John Smith",
"email": "john@techcorp.com",
"phoneNumber": "+33123456789",
"currentCompany": "Tech Corp"
},
"assignedTo": null,
"createdAt": "2024-11-08T10:30:00.000Z",
"updatedAt": "2024-11-08T15:30:00.000Z"
}
}

The deals API provides powerful filtering capabilities.

Filter by Status

Get only open deals:

curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://staging.isao.io/api/external/deals?status=open"

Filter by Stage

Get deals in specific stage:

curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://staging.isao.io/api/external/deals?stage=negotiation"

Filter by Date Range

Get deals created since a specific date:

curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://staging.isao.io/api/external/deals?createdSince=2024-11-01T00:00:00.000Z"

Get deals updated in the last week:

curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://staging.isao.io/api/external/deals?updatedSince=2024-11-01T00:00:00.000Z"

Combined Filtering

Combine multiple filters:

curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://staging.isao.io/api/external/deals?status=open&stage=proposal&createdSince=2024-11-01T00:00:00.000Z"

❌ Error Handling

Common Error Responses

401 Unauthorized

{
"success": false,
"error": "API key required in Authorization header",
"message": "Please provide a valid API key in the format: Authorization: Bearer YOUR_API_KEY"
}

403 Forbidden

{
"success": false,
"error": "Permission denied: deals:read required",
"message": "This API key does not have the required permission: deals:read",
"requiredPermission": "deals:read",
"availablePermissions": ["contacts:read", "contacts:write"]
}

404 Not Found

{
"success": false,
"error": "Deal not found"
}

422 Validation Error

{
"success": false,
"error": "Validation failed",
"details": {
"title": "Title is required",
"value": "Value must be a positive number"
}
}

💻 Code Examples

JavaScript/Node.js

const axios = require('axios');

class DealsAPI {
constructor(apiKey) {
this.client = axios.create({
baseURL: 'https://staging.isao.io/api/external',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
}

// List all deals with filtering
async listDeals(options = {}) {
const params = {
page: options.page || 1,
limit: options.limit || 50,
status: options.status,
stage: options.stage,
createdSince: options.createdSince,
updatedSince: options.updatedSince
};

const response = await this.client.get('/deals', { params });
return response.data;
}

// Get deal by ID
async getDeal(dealId) {
const response = await this.client.get(`/deals/${dealId}`);
return response.data;
}

// Create new deal
async createDeal(dealData) {
const response = await this.client.post('/deals', dealData);
return response.data;
}

// Update deal status
async updateDealStatus(dealId, statusData) {
const response = await this.client.put(`/deals/${dealId}/status`, statusData);
return response.data;
}

// Mark deal as won
async markDealAsWon(dealId, closeDate = new Date().toISOString()) {
return this.updateDealStatus(dealId, {
status: 'won',
actualCloseDate: closeDate
});
}

// Mark deal as lost
async markDealAsLost(dealId, closeDate = new Date().toISOString()) {
return this.updateDealStatus(dealId, {
status: 'lost',
actualCloseDate: closeDate
});
}

// Get deals by status
async getDealsByStatus(status, limit = 50) {
return this.listDeals({ status, limit });
}

// Get deals in pipeline (open deals)
async getPipelineDeals() {
return this.listDeals({ status: 'open' });
}
}

// Usage example
const dealsAPI = new DealsAPI(process.env.ISAO_API_KEY);

async function example() {
try {
// Create a deal
const newDeal = await dealsAPI.createDeal({
title: "Mobile App Development",
description: "Native iOS and Android app development",
value: 75000,
currency: "EUR",
stage: "discovery",
priority: "high",
probability: 40,
expectedCloseDate: "2024-12-31",
source: "referral",
contactId: "contact_123"
});
console.log('Created deal:', newDeal);

// List pipeline deals
const pipelineDeals = await dealsAPI.getPipelineDeals();
console.log('Pipeline deals:', pipelineDeals);

// Update deal stage
const updatedDeal = await dealsAPI.updateDealStatus(newDeal.data.id, {
stage: "proposal",
probability: 60
});
console.log('Updated deal:', updatedDeal);

// Mark deal as won
const wonDeal = await dealsAPI.markDealAsWon(newDeal.data.id);
console.log('Won deal:', wonDeal);

} catch (error) {
console.error('Error:', error.response?.data || error.message);
}
}

Python

import requests
from typing import Optional, Dict, List
from datetime import datetime

class DealsAPI:
def __init__(self, api_key: str):
self.base_url = "https://staging.isao.io/api/external"
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}

def list_deals(self, page: int = 1, limit: int = 50,
status: Optional[str] = None,
stage: Optional[str] = None,
created_since: Optional[str] = None,
updated_since: Optional[str] = None) -> Dict:
params = {
"page": page,
"limit": limit,
"status": status,
"stage": stage,
"createdSince": created_since,
"updatedSince": updated_since
}
# Remove None values
params = {k: v for k, v in params.items() if v is not None}

response = requests.get(f"{self.base_url}/deals",
headers=self.headers, params=params)
response.raise_for_status()
return response.json()

def get_deal(self, deal_id: str) -> Dict:
response = requests.get(f"{self.base_url}/deals/{deal_id}",
headers=self.headers)
response.raise_for_status()
return response.json()

def create_deal(self, deal_data: Dict) -> Dict:
response = requests.post(f"{self.base_url}/deals",
json=deal_data, headers=self.headers)
response.raise_for_status()
return response.json()

def update_deal_status(self, deal_id: str, status_data: Dict) -> Dict:
response = requests.put(f"{self.base_url}/deals/{deal_id}/status",
json=status_data, headers=self.headers)
response.raise_for_status()
return response.json()

def mark_deal_as_won(self, deal_id: str, close_date: Optional[str] = None) -> Dict:
if close_date is None:
close_date = datetime.now().isoformat()

return self.update_deal_status(deal_id, {
"status": "won",
"actualCloseDate": close_date
})

def mark_deal_as_lost(self, deal_id: str, close_date: Optional[str] = None) -> Dict:
if close_date is None:
close_date = datetime.now().isoformat()

return self.update_deal_status(deal_id, {
"status": "lost",
"actualCloseDate": close_date
})

def get_deals_by_status(self, status: str, limit: int = 50) -> Dict:
return self.list_deals(status=status, limit=limit)

def get_pipeline_deals(self) -> Dict:
"""Get all open deals in the pipeline"""
return self.list_deals(status="open")

# Usage example
import os

deals_api = DealsAPI(os.getenv("ISAO_API_KEY"))

try:
# Create a deal
new_deal = deals_api.create_deal({
"title": "E-commerce Platform",
"description": "Custom e-commerce solution with integrations",
"value": 120000,
"currency": "EUR",
"stage": "qualification",
"priority": "high",
"probability": 50,
"expectedCloseDate": "2025-02-28",
"source": "website",
"contactId": "contact_456"
})
print("Created deal:", new_deal)

# List pipeline deals
pipeline = deals_api.get_pipeline_deals()
print(f"Pipeline has {pipeline['meta']['total']} open deals")

# Get won deals from this month
won_deals = deals_api.get_deals_by_status("won", limit=100)
print(f"Won {len(won_deals['data'])} deals")

except requests.exceptions.HTTPError as e:
print(f"HTTP Error: {e.response.status_code}")
print(f"Error details: {e.response.json()}")

PHP

<?php

class DealsAPI {
private $baseUrl = 'https://staging.isao.io/api/external';
private $headers;

public function __construct($apiKey) {
$this->headers = [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
];
}

private function makeRequest($method, $endpoint, $data = null, $params = []) {
$url = $this->baseUrl . $endpoint;
if (!empty($params)) {
$url .= '?' . http_build_query(array_filter($params));
}

$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $this->headers,
]);

if ($data) {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
}

$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);

$result = json_decode($response, true);

if ($httpCode >= 400) {
throw new Exception("API Error ({$httpCode}): " . ($result['error'] ?? 'Unknown error'));
}

return $result;
}

public function listDeals($options = []) {
$params = [
'page' => $options['page'] ?? 1,
'limit' => $options['limit'] ?? 50,
'status' => $options['status'] ?? null,
'stage' => $options['stage'] ?? null,
'createdSince' => $options['createdSince'] ?? null,
'updatedSince' => $options['updatedSince'] ?? null
];

return $this->makeRequest('GET', '/deals', null, $params);
}

public function getDeal($dealId) {
return $this->makeRequest('GET', "/deals/{$dealId}");
}

public function createDeal($dealData) {
return $this->makeRequest('POST', '/deals', $dealData);
}

public function updateDealStatus($dealId, $statusData) {
return $this->makeRequest('PUT', "/deals/{$dealId}/status", $statusData);
}

public function markDealAsWon($dealId, $closeDate = null) {
if ($closeDate === null) {
$closeDate = date('c'); // ISO 8601 format
}

return $this->updateDealStatus($dealId, [
'status' => 'won',
'actualCloseDate' => $closeDate
]);
}

public function markDealAsLost($dealId, $closeDate = null) {
if ($closeDate === null) {
$closeDate = date('c'); // ISO 8601 format
}

return $this->updateDealStatus($dealId, [
'status' => 'lost',
'actualCloseDate' => $closeDate
]);
}

public function getDealsByStatus($status, $limit = 50) {
return $this->listDeals(['status' => $status, 'limit' => $limit]);
}

public function getPipelineDeals() {
return $this->listDeals(['status' => 'open']);
}
}

// Usage example
try {
$dealsAPI = new DealsAPI(getenv('ISAO_API_KEY'));

// Create a deal
$newDeal = $dealsAPI->createDeal([
'title' => 'Digital Marketing Campaign',
'description' => '6-month digital marketing strategy and execution',
'value' => 45000,
'currency' => 'EUR',
'stage' => 'proposal',
'priority' => 'medium',
'probability' => 70,
'expectedCloseDate' => '2024-12-15',
'source' => 'referral',
'contactId' => 'contact_789'
]);
echo "Created deal: " . json_encode($newDeal) . "\n";

// List pipeline deals
$pipeline = $dealsAPI->getPipelineDeals();
echo "Pipeline has " . $pipeline['meta']['total'] . " open deals\n";

// Mark deal as won
$wonDeal = $dealsAPI->markDealAsWon($newDeal['data']['id']);
echo "Marked deal as won: " . json_encode($wonDeal) . "\n";

} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>

🔔 Webhook Events

When deals are created or updated, webhook events are triggered for real-time integrations:

Available Events

  • deal.created - When a new deal is created
  • deal.updated - When a deal is updated
  • deal.stage_changed - When a deal moves to a new stage
  • deal.won - When a deal is marked as won
  • deal.lost - When a deal is marked as lost

Example Webhook Payload

{
"event": "deal.won",
"data": {
"id": "deal_789",
"title": "Website Redesign Project",
"value": 25000,
"currency": "EUR",
"stage": "closed-won",
"status": "won",
"wonAt": "2024-11-08T15:30:00.000Z",
"contact": {
"id": "contact_456",
"name": "John Smith",
"email": "john@techcorp.com"
}
}
}

✅ Best Practices

  1. Link deals to contacts for better relationship tracking
  2. Use meaningful stage names that reflect your sales process
  3. Set realistic probabilities to improve forecast accuracy
  4. Track expected close dates for pipeline management
  5. Use webhooks for real-time deal updates in your CRM
  6. Set appropriate currencies for international deals
  7. Monitor deal progression through stage changes