API и Webhooks: Интеграция TenancyDesk с вашими системами

Обновлено 14 июля 2026 г.

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 ключа:

  1. НастройкиИнтеграцииAPI Keys
  2. Нажмите Сгенерировать новый API ключ
  3. Имя: "Production Integration" (описательное)
  4. Области: Выберите разрешения (read:deals, write:parties и т.д.)
  5. Скопируйте ключ немедленно (показывается только один раз)

Заголовок аутентификации:

curl -H "Authorization: Bearer sk_live_abc123xyz..." \
  https://api.tenancydesk.com/v1/deals

Безопасность:

  • Безопасное хранение (переменные окружения, не код)
  • Ротация каждые 90 дней
  • Используйте разные ключи для dev/staging/prod
  • Немедленный отзыв при компрометации

JWT Bearer Tokens (OAuth 2.0 - Enterprise)

Для операций в контексте пользователя:

  1. Получите токен доступа через OAuth flow
  2. Включите в заголовок Authorization
  3. Срок действия токена: 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 лимит
Enterprise30015,000600

Заголовки лимитов (возвращаются в каждом ответе):

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

Реализация:

  1. Подпишитесь на webhook deal.created
  2. При получении вызовите 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

Реализация:

  1. Подпишитесь на payment.received
  2. Вызовите QuickBooks API для создания счёта
  3. Обновите сделку ID счёта QB

Паттерн 3: Кастомная панель управления

Сценарий использования: Real-time аналитическая панель

Реализация:

  1. Опрашивайте endpoint /deals каждые 5 минут
  2. Агрегируйте данные (сделки по стадиям, выручка)
  3. Отображайте в кастомной панели

Обработка ошибок

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 интеграции:

  1. Обработка ошибок: Перехватывайте все HTTP ошибки, реализуйте логику повторов
  2. Idempotency: Используйте заголовок Idempotency-Key для POST запросов
  3. Лимиты запросов: Уважайте лимиты, реализуйте backoff
  4. Безопасность Webhook: Всегда верифицируйте HMAC подписи
  5. Логирование: Логируйте все API вызовы для отладки
  6. Мониторинг: Отслеживайте латентность API и частоту ошибок
  7. Версионирование: Используйте заголовок версии 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.

Подписываясь, вы соглашаетесь получать письма от TenancyDesk. Отписаться можно в любой момент.

Нужна помощь?

Наша команда поддержки готова помочь с любыми вопросами.

Связаться с нами