Enterprise-grade REST API и real-time webhooks обеспечивают бесшовную интеграцию между TenancyDesk и вашими существующими бизнес-системами. Создавайте кастомные рабочие процессы, автоматизируйте синхронизацию данных и разрабатывайте мощные интеграции.
Обзор API
Архитектура
RESTful API следуя отраслевым стандартам:
- URL на основе ресурсов (
/deals,/parties,/documents) - HTTP методы: GET, POST, PUT, PATCH, DELETE
- JSON тела запросов/ответов
- Стандартные HTTP статус коды
- Спецификация OpenAPI 3.0 доступна
Base URL: https://api.tenancydesk.com/v1/
Документация API: https://docs.tenancydesk.com/api (интерактивный Swagger UI)
Аутентификация
API Keys (рекомендуется)
Генерация API ключа:
- Настройки → Интеграции → API Keys
- Нажмите Сгенерировать новый API ключ
- Имя: "Production Integration" (описательное)
- Области: Выберите разрешения (read:deals, write:parties и т.д.)
- Скопируйте ключ немедленно (показывается только один раз)
Заголовок аутентификации:
curl -H "Authorization: Bearer sk_live_abc123xyz..." \
https://api.tenancydesk.com/v1/deals
Безопасность:
- Безопасное хранение (переменные окружения, не код)
- Ротация каждые 90 дней
- Используйте разные ключи для dev/staging/prod
- Немедленный отзыв при компрометации
JWT Bearer Tokens (OAuth 2.0 - Enterprise)
Для операций в контексте пользователя:
- Получите токен доступа через OAuth flow
- Включите в заголовок Authorization
- Срок действия токена: 1 час (refresh token: 7 дней)
Используйте когда: Создаёте приложения, где пользователи аутентифицируются своими учётными записями TenancyDesk
Основные API Endpoints
Deals API
Список всех сделок:
GET /v1/deals?status=active&limit=50&offset=0
Response 200:
{
"data": [
{
"id": "deal_abc123",
"property_address": "Unit 1205, Churchill Tower, Business Bay",
"tenant_name": "Омар Халид",
"annual_rent": 85000,
"status": "active",
"ejari_number": "12345678",
"created_at": "2025-01-15T10:30:00Z"
}
],
"pagination": {
"total": 127,
"limit": 50,
"offset": 0,
"has_more": true
}
}
Создание сделки:
POST /v1/deals
Content-Type: application/json
{
"property_address": "Unit 1205, Churchill Tower",
"tenant": {
"name": "Омар Халид",
"emirates_id": "784-1988-1234567-1",
"mobile": "+971501234567"
},
"annual_rent": 85000,
"start_date": "2025-02-01",
"payment_schedule": "4_cheques"
}
Response 201:
{
"data": {
"id": "deal_abc123",
"status": "draft",
...
}
}
Получение деталей сделки:
GET /v1/deals/{deal_id}
Response 200: Полный объект сделки с вложенными сторонами, документами, платежами
Обновление сделки:
PATCH /v1/deals/{deal_id}
{
"annual_rent": 90000,
"status": "active"
}
Parties API
Создание арендатора:
POST /v1/parties/tenants
{
"name": "Фатима Али",
"emirates_id": "784-1990-2345678-2",
"mobile": "+971509876543",
"email": "fatima@email.com"
}
Поиск сторон:
GET /v1/parties?search=Ахмед&type=tenant&limit=20
Documents API
Загрузка документа:
POST /v1/deals/{deal_id}/documents
Content-Type: multipart/form-data
file: @emirates-id-front.pdf
category: tenant_emirates_id
Response 201:
{
"data": {
"id": "doc_xyz789",
"category": "tenant_emirates_id",
"filename": "emirates-id-front.pdf",
"extracted_data": {
"name": "Омар Мохаммед Халид",
"id_number": "784-1988-1234567-1",
"expiry_date": "2028-12-31"
},
"verification_status": "verified"
}
}
Payments API
Список графика платежей:
GET /v1/deals/{deal_id}/payments
Response: Массив платёжных взносов с датами погашения, суммами, статусами
Отметка платежа как полученного:
PATCH /v1/payments/{payment_id}
{
"status": "paid",
"paid_date": "2025-02-01",
"transaction_ref": "CHQ-12345"
}
Лимиты запросов
Доступ к API только на тарифе Enterprise. Лимиты по умолчанию:
| Тариф | Запросов/мин | Запросов/час | Burst лимит |
|---|---|---|---|
| Enterprise | 300 | 15,000 | 600 |
Заголовки лимитов (возвращаются в каждом ответе):
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1640000000
429 Too Many Requests:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Лимит запросов превышен. Повторите через 60 секунд.",
"retry_after": 60
}
}
Лучшая практика: Реализуйте exponential backoff при получении 429
Webhooks
Обзор
Real-time уведомления при возникновении событий в TenancyDesk. Polling не требуется.
Гарантия доставки: At-least-once доставка с автоматическими повторами
Верификация подписи: HMAC-SHA256 для безопасности
Настройка Webhook
1. Создание webhook endpoint (ваш сервер):
# Пример Flask
from flask import Flask, request
import hmac
import hashlib
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
# Верификация подписи
signature = request.headers.get('X-TenancyDesk-Signature')
payload = request.data
# Вычисление ожидаемой подписи
secret = 'your_webhook_secret'
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
if signature != expected:
return 'Недействительная подпись', 401
# Обработка события
event = request.json
if event['type'] == 'payment.overdue':
send_reminder(event['data'])
return 'OK', 200
2. Регистрация webhook в TenancyDesk:
POST /v1/webhooks
{
"url": "https://your-domain.com/webhook",
"events": ["payment.overdue", "deal.status_changed"],
"description": "Production webhook для напоминаний о платежах"
}
Response 201:
{
"data": {
"id": "wh_abc123",
"url": "https://your-domain.com/webhook",
"secret": "whsec_xyz789...", // Используйте для верификации подписи
"events": ["payment.overdue", "deal.status_changed"],
"status": "active"
}
}
События Webhook
TenancyDesk отправляет 21 тип событий в шести категориях.
События сделки (5):
deal.created- Создана новая сделкаdeal.updated- Изменились детали сделкиdeal.status_changed- Изменился статус сделки (например, переход в Активную)deal.completed- Сделка завершенаdeal.cancelled- Сделка отменена
События платежей (4):
payment.created- Создана запись в графике платежейpayment.received- Платёж отмечен как оплаченныйpayment.overdue- Пропущен срок платежаpayment.bounced- Чек отклонён
События документов (4):
document.uploaded- Добавлен новый документdocument.verified- Документ проверенdocument.rejected- Верификация не удаласьdocument.expiring- Срок действия документа истекает
События сторон (3):
party.created- Добавлена сторона (арендатор или владелец)party.updated- Изменились данные стороныparty.kyc_completed- Завершена проверка личности стороны (KYC)
События задач (3):
task.created- Задача созданаtask.completed- Задача выполненаtask.overdue- Просрочен срок задачи
События соответствия (2):
compliance.issue_created- Обнаружена проблема соответствияcompliance.issue_resolved- Проблема соответствия решена
Для регистрации Ejari/Tawtheeq нет отдельных webhook-событий — TenancyDesk не интегрируется напрямую с DLD. Когда ваша команда указывает регистрационный номер или обновляет статус регистрации сделки, это изменение доставляется через событие
deal.updated, указанное выше.
Структура Webhook Payload
Стандартная обёртка:
{
"id": "evt_abc123",
"type": "payment.overdue",
"created_at": "2025-12-26T10:30:00Z",
"data": {
"deal_id": "deal_xyz789",
"payment_id": "pay_123",
"tenant_name": "Омар Халид",
"amount": 21250,
"due_date": "2025-01-01",
"cheque_number": "CHQ-001"
},
"company_id": "comp_abc"
}
Логика повторов
TenancyDesk следует индустриальному стандарту Hook0:
Быстрые повторы (первые 10 попыток):
- Попытка 1: Немедленно
- Попытка 2: 5 секунд
- Попытка 3: 25 секунд
- Попытка 4: 125 секунд (2 мин)
- Попытки 5-10: Exponential backoff до 5 минут
Медленные повторы (попытки 11-60):
- Каждый 1 час
- До 60 попыток всего (2.5 дня)
Отказ после: 60 неудачных попыток
Ваш endpoint должен:
- Отвечать в течение 5 секунд
- Возвращать статус код 200-299
- Обрабатывать асинхронно (не блокировать ответ)
Безопасность
Верификация подписи Webhook (HMAC-SHA256):
Заголовок: X-TenancyDesk-Signature
Вычисление ожидаемой подписи:
import hmac
import hashlib
def verify_webhook(payload, signature, secret):
expected = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# Использование
if not verify_webhook(request.body, request.headers['X-TenancyDesk-Signature'], webhook_secret):
return 'Неавторизовано', 401
Лучшие практики безопасности:
- Всегда верифицируйте подписи
- Используйте только HTTPS (webhooks отклоняются если HTTP)
- Реализуйте idempotency (одно событие может быть доставлено дважды)
- Логируйте все попытки webhook для отладки
Распространённые паттерны интеграции
Паттерн 1: Синхронизация сделок с CRM
Сценарий использования: Каждая сделка TenancyDesk автоматически создаётся в Zoho CRM
Реализация:
- Подпишитесь на webhook
deal.created - При получении вызовите Zoho CRM API:
# Обработчик webhook TenancyDesk if event['type'] == 'deal.created': deal = event['data'] zoho.create_lead({ 'name': deal['tenant_name'], 'value': deal['annual_rent'], 'source': 'TenancyDesk' })
Паттерн 2: Автоматизация бухгалтерии
Сценарий использования: Платёж получен → создать счёт в QuickBooks
Реализация:
- Подпишитесь на
payment.received - Вызовите QuickBooks API для создания счёта
- Обновите сделку ID счёта QB
Паттерн 3: Кастомная панель управления
Сценарий использования: Real-time аналитическая панель
Реализация:
- Опрашивайте endpoint
/dealsкаждые 5 минут - Агрегируйте данные (сделки по стадиям, выручка)
- Отображайте в кастомной панели
Обработка ошибок
HTTP статус коды:
200 OK- Успех201 Created- Ресурс создан400 Bad Request- Недействительные параметры401 Unauthorized- Недействительный/отсутствующий API ключ403 Forbidden- Недостаточно разрешений404 Not Found- Ресурс не существует422 Unprocessable Entity- Ошибка валидации429 Too Many Requests- Лимит запросов превышен500 Internal Server Error- Проблема сервера (повторите)
Формат ответа об ошибке:
{
"error": {
"code": "validation_failed",
"message": "Годовая аренда должна быть больше 0",
"field": "annual_rent",
"docs_url": "https://docs.tenancydesk.com/api/errors#validation"
}
}
Обработка ошибок:
response = requests.post(url, headers=headers, json=data)
if response.status_code == 422:
error = response.json()['error']
print(f"Ошибка валидации в {error['field']}: {error['message']}")
elif response.status_code == 429:
retry_after = int(response.headers['Retry-After'])
time.sleep(retry_after)
# Повторить запрос
elif response.status_code >= 500:
# Exponential backoff для повтора
pass
Лучшие практики
Production-ready интеграции:
- Обработка ошибок: Перехватывайте все HTTP ошибки, реализуйте логику повторов
- Idempotency: Используйте заголовок
Idempotency-Keyдля POST запросов - Лимиты запросов: Уважайте лимиты, реализуйте backoff
- Безопасность Webhook: Всегда верифицируйте HMAC подписи
- Логирование: Логируйте все API вызовы для отладки
- Мониторинг: Отслеживайте латентность API и частоту ошибок
- Версионирование: Используйте заголовок версии API при фиксации версии
Пример production-ready запроса:
import requests
import time
def create_deal_with_retry(deal_data, max_retries=3):
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
'Idempotency-Key': f'deal_{uuid4()}',
'X-API-Version': '2025-12-01'
}
for attempt in range(max_retries):
try:
response = requests.post(
'https://api.tenancydesk.com/v1/deals',
headers=headers,
json=deal_data,
timeout=10
)
if response.status_code == 201:
return response.json()['data']
elif response.status_code == 429:
wait = int(response.headers.get('Retry-After', 60))
time.sleep(wait)
continue
elif response.status_code >= 500:
time.sleep(2 ** attempt) # Exponential backoff
continue
else:
response.raise_for_status()
except requests.exceptions.Timeout:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
raise Exception("Превышено максимальное число повторов")
Создавайте надёжные интеграции, которые масштабируются вместе с вашим бизнесом. TenancyDesk API спроектирован для интеграций производственного уровня на проверенной облачной инфраструктуре.
Была ли эта статья полезна?
Похожие статьи
Оставайтесь в курсе
Получайте последние руководства, советы и обновления на ваш email.