# Dokumentasi API TagihanAI

Dokumentasi API TagihanAI untuk integrasi invoice, billing, payment, webhook, autentikasi API key, rate limit, dan approval aplikasi developer.

Canonical URL: https://www.tagihanai.com/api-documentation
Base URL: https://www.tagihanai.com/api/v1
API version: v1
Last updated: 2026-06-30
OpenAPI: https://www.tagihanai.com/openapi.json

## Ringkasan

API TagihanAI menyediakan akses terprogram ke data invoice, billing, pembayaran, langganan, pihak, pengaturan workspace, dan webhook. Semua endpoint resource memakai JSON. Endpoint non-public membutuhkan API key di header `Authorization: Bearer <api_key>`.

## Scope API

- `transactions:read`: Read transactions (invoices, bills, credit notes, debit notes)
- `transactions:write`: Create, update, delete, and transition transactions
- `parties:read`: Read customers and vendors
- `parties:write`: Create, update, delete customers and vendors
- `payments:read`: Read payment transactions
- `payments:write`: Confirm and manage payments
- `subscriptions:read`: Read subscriptions
- `subscriptions:write`: Create, update, and transition subscriptions
- `webhooks:read`: Read webhook subscriptions and delivery logs
- `webhooks:write`: Create, update, delete webhook subscriptions and send test events
- `settings:read`: Read workspace document, email, and invoice style settings
- `settings:write`: Update workspace document, email, and invoice style settings except document numbering

## Rate Limit

- standard: 1,000 requests/hour
- premium: 5,000 requests/hour
- enterprise: 20,000 requests/hour

## Error Types

- 400 `bad-request`: Malformed JSON or missing required parameters
- 401 `unauthorized`: Missing or invalid API key
- 403 `forbidden`: Valid key but insufficient scopes
- 404 `not-found`: Resource does not exist in this workspace
- 409 `conflict`: Optimistic locking version mismatch
- 422 `validation-failed`: Semantic validation errors in request body
- 429 `rate-limit-exceeded`: Rate limit exceeded, retry after delay
- 500 `internal-error`: Unexpected server error

## FAQ

### Apa itu API TagihanAI?

API TagihanAI adalah REST API berbasis JSON untuk mengakses data invoice, bill, pembayaran, langganan, webhook, pihak, dan pengaturan workspace secara terprogram.

### Apa base URL API TagihanAI?

Base URL production adalah https://www.tagihanai.com/api/v1. Endpoint resource terautentikasi dipanggil relatif terhadap base URL ini, misalnya https://www.tagihanai.com/api/v1/transactions.

### Bagaimana autentikasi request API TagihanAI?

Kirim API key di header Authorization sebagai Bearer token. API key terikat ke workspace dan dibatasi oleh scope yang sudah disetujui.

### Bagaimana cara membuat invoice lewat API TagihanAI?

Gunakan POST /transactions dengan type INVOICE, total, data counterparty, line item opsional, serta external_source dan external_invoice_id untuk sinkronisasi idempotent.

### Event webhook apa saja yang tersedia?

TagihanAI mendukung event transaksi, pembayaran, langganan, dan pihak, termasuk transaction.created, transaction.paid, payment.completed, subscription.activated, dan party.updated.

### Bagaimana verifikasi signature webhook TagihanAI?

Baca header X-TagihanAI-Signature, susun payload bertanda tangan sebagai timestamp.raw_body, lalu verifikasi signature HMAC-SHA256 memakai webhook signing secret.

### Apakah aplikasi pihak ketiga perlu approval sebelum memakai credential production?

Ya. Developer pihak ketiga mendaftarkan aplikasi, menyelesaikan review administrator, menguji dengan credential sandbox, lalu menerima credential live setelah disetujui.

## Resources

## Transactions

Unified document model supporting Invoice, Bill, Credit Note, and Debit Note types. External invoice apps can use external_source + external_invoice_id for idempotent invoice sync.

### GET /transactions

Retrieve a paginated list of transactions with optional filters.

- Full path: `/api/v1/transactions`
- Required scope: `transactions:read`

Query parameters:
- `type` (string): Filter by type: INVOICE, BILL, CREDIT_NOTE, DEBIT_NOTE
- `status` (string): Filter by transaction status
- `payment_status` (string): Filter: UNPAID, PARTIAL, PAID
- `date_from` (string): Filter by document_date >= (ISO 8601)
- `date_to` (string): Filter by document_date <= (ISO 8601)
- `due_before` (string): Filter by due_date <= (ISO 8601)
- `search` (string): Search document_number and receiver_name
- `page` (integer): Page number (default: 1)
- `per_page` (integer): Items per page (default: 20, max: 100)
- `sort` (string): Sort field:direction, e.g. created_at:desc
- `party_id` (string): Filter by counterparty UUID

Example response:

```json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": "INVOICE",
      "document_number": "INV-2026-0042",
      "document_date": "2026-02-10",
      "due_date": "2026-03-10",
      "status": "SENT",
      "payment_status": "UNPAID",
      "total": 1500000,
      "paid_amount": 0,
      "receiver_name": "PT Maju Jaya",
      "description": "Web development services",
      "created_at": "2026-02-10T08:30:00Z"
    }
  ],
  "pagination": {
    "total": 142,
    "page": 1,
    "per_page": 20,
    "has_more": true
  }
}
```

### POST /transactions

Create or idempotently upsert a transaction. External invoice apps should send external_source and external_invoice_id to avoid duplicates.

- Full path: `/api/v1/transactions`
- Required scope: `transactions:write`

Request body fields:
- `type` (string, required): Transaction type
- `amount` (number): Total amount. Use total for new integrations; amount is accepted for compatibility.
- `total` (number, required): Total amount
- `document_number` (string): Document number (auto-generated if omitted)
- `document_date` (string): Document date (YYYY-MM-DD)
- `due_date` (string): Due date (YYYY-MM-DD)
- `counterparty_name` (string): Customer or vendor name
- `counterparty_email` (string): Customer or vendor email
- `counterparty_phone` (string): Customer or vendor phone/WhatsApp
- `counterparty_id` (string): Counterparty UUID (alternative to counterparty_name)
- `external_source` (string): Workspace integration source slug, e.g. custom_app
- `external_invoice_id` (string): Stable invoice ID from the source app
- `pdf_url` (string): Optional official PDF snapshot URL from the source app
- `line_items` (array): Optional line items with product_name/name, quantity, unit_price/price, description
- `description` (string): Description or notes
- `metadata` (object): Custom metadata (key-value pairs)

Example request body:

```json
{
  "type": "INVOICE",
  "external_source": "custom_app",
  "external_invoice_id": "INV-EXT-1001",
  "total": 1500000,
  "document_date": "2026-02-10",
  "due_date": "2026-03-10",
  "counterparty_name": "PT Maju Jaya",
  "counterparty_email": "finance@majujaya.example",
  "description": "Web development services"
}
```

Example response:

```json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "INVOICE",
    "status": "DRAFT",
    "payment_status": "UNPAID",
    "total": 1500000,
    "living_invoice_url": "https://tagihanai.com/invoice/public/abc123...",
    "created_at": "2026-02-10T08:30:00Z"
  }
}
```

### GET /transactions/:id

Retrieve a single transaction by ID.

- Full path: `/api/v1/transactions/:id`
- Required scope: `transactions:read`

Example response:

```json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "INVOICE",
    "document_number": "INV-2026-0042",
    "status": "SENT",
    "payment_status": "UNPAID",
    "total": 1500000,
    "paid_amount": 0,
    "receiver_name": "PT Maju Jaya",
    "metadata": {}
  }
}
```

### PATCH /transactions/:id

Update a transaction. Draft transactions update in place; sent unpaid transactions are treated as revisions. Financial fields are locked after partial or full payment.

- Full path: `/api/v1/transactions/:id`
- Required scope: `transactions:write`

Request body fields:
- `document_number` (string): Document number
- `document_date` (string): Document date (YYYY-MM-DD)
- `due_date` (string): Due date (YYYY-MM-DD)
- `counterparty_name` (string): Customer or vendor name
- `total` (number): Total amount
- `amount` (number): Total amount alias for compatibility
- `line_items` (array): Optional replacement line items
- `description` (string): Description
- `metadata` (object): Custom metadata (key-value pairs)

Example request body:

```json
{
  "due_date": "2026-04-10",
  "description": "Updated description"
}
```

Example response:

```json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "due_date": "2026-04-10"
  }
}
```

### DELETE /transactions/:id

Cancel a transaction. Paid and partially paid transactions cannot be deleted; use a reversal, credit note, or refund flow instead.

- Full path: `/api/v1/transactions/:id`
- Required scope: `transactions:write`

### POST /transactions/:id/actions/:action

Execute a state transition on a transaction. Available actions for external invoice apps: send, cancel, mark_paid.

- Full path: `/api/v1/transactions/:id/actions/:action`
- Required scope: `transactions:write`

Request body fields:
- `expected_version` (integer): Optimistic locking version (from metadata.version)
- `payload` (object): Additional data for the transition

Example request body:

```json
{
  "expected_version": 3,
  "payload": {
    "notes": "Approved by finance team"
  }
}
```

Example response:

```json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "SENT",
    "payment_status": "UNPAID",
    "living_invoice_url": "https://tagihanai.com/invoice/public/abc123..."
  }
}
```

### GET /transactions/:id/payments

Retrieve a paginated list of payments linked to a specific transaction.

- Full path: `/api/v1/transactions/:id/payments`
- Required scope: `transactions:read`

Query parameters:
- `page` (integer): Page number
- `per_page` (integer): Items per page

Example response:

```json
{
  "data": [
    {
      "id": "uuid",
      "transaction_id": "550e8400-e29b-41d4-a716-446655440000",
      "status": "PAID",
      "amount": 500000,
      "payment_method": "bank_transfer",
      "paid_at": "2026-02-10T10:00:00Z"
    }
  ],
  "pagination": {
    "total": 2,
    "page": 1,
    "per_page": 20,
    "has_more": false
  }
}
```

### POST /transactions/:id/payments

Record an idempotent manual payment from the source app. Use external_source and external_payment_id when the payment was created outside TagihanAi.

- Full path: `/api/v1/transactions/:id/payments`
- Required scope: `payments:write`

Request body fields:
- `external_source` (string): Workspace integration source slug
- `external_payment_id` (string): Stable payment ID from the source app
- `amount` (number, required): Payment amount
- `payment_date` (string): Payment date (YYYY-MM-DD or ISO 8601)
- `payment_method` (string): Payment method label, e.g. bank_transfer
- `notes` (string): Optional payment notes
- `metadata` (object): Custom metadata

Example request body:

```json
{
  "external_source": "custom_app",
  "external_payment_id": "PAY-EXT-7788",
  "amount": 1500000,
  "payment_date": "2026-06-30",
  "payment_method": "bank_transfer",
  "notes": "Recorded manually in source app"
}
```

Example response:

```json
{
  "data": {
    "payment": {
      "id": "pay_123",
      "status": "PAID",
      "amount": 1500000
    },
    "transaction": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "payment_status": "PAID"
    },
    "paid_amount": 1500000,
    "outstanding_amount": 0,
    "payment_status": "PAID"
  }
}
```

## Parties

Unified API for customers and vendors. Use the role parameter to filter.

### GET /parties

List customers and/or vendors.

- Full path: `/api/v1/parties`
- Required scope: `parties:read`

Query parameters:
- `role` (string): Filter: customer, vendor (omit for both)
- `search` (string): Search name, email, phone
- `page` (integer): Page number
- `per_page` (integer): Items per page

Example response:

```json
{
  "data": [
    {
      "id": "uuid",
      "role": "customer",
      "name": "PT Maju Jaya",
      "email": "billing@majujaya.com"
    }
  ],
  "pagination": {
    "total": 50,
    "page": 1,
    "per_page": 20,
    "has_more": true
  }
}
```

### POST /parties

Create a new customer or vendor.

- Full path: `/api/v1/parties`
- Required scope: `parties:write`

Request body fields:
- `name` (string, required): Name
- `role` (string, required): Role: customer or vendor
- `email` (string): Email address
- `phone` (string): Phone number
- `address` (string): Address

Example request body:

```json
{
  "name": "PT Maju Jaya",
  "role": "customer",
  "email": "billing@majujaya.com"
}
```

Example response:

```json
{
  "data": {
    "id": "uuid",
    "role": "customer",
    "name": "PT Maju Jaya",
    "email": "billing@majujaya.com"
  }
}
```

### GET /parties/:id

Retrieve a single customer or vendor by ID.

- Full path: `/api/v1/parties/:id`
- Required scope: `parties:read`

Example response:

```json
{
  "data": {
    "id": "uuid",
    "role": "vendor",
    "name": "Vendor Corp",
    "email": "vendor@corp.com"
  }
}
```

### PATCH /parties/:id

Update a customer or vendor.

- Full path: `/api/v1/parties/:id`
- Required scope: `parties:write`

Request body fields:
- `name` (string): Name
- `email` (string): Email
- `phone` (string): Phone
- `address` (string): Address

Example request body:

```json
{
  "email": "new@email.com"
}
```

Example response:

```json
{
  "data": {
    "id": "uuid",
    "name": "PT Maju Jaya",
    "email": "new@email.com"
  }
}
```

### DELETE /parties/:id

Remove a customer or vendor.

- Full path: `/api/v1/parties/:id`
- Required scope: `parties:write`

## Payments

Payment transactions linked to invoices and bills.

### GET /payments

List payment transactions.

- Full path: `/api/v1/payments`
- Required scope: `payments:read`

Query parameters:
- `status` (string): Filter: PENDING, PROCESSING, PAID, FAILED, EXPIRED, CANCELLED
- `transaction_id` (string): Filter by parent transaction UUID
- `date_from` (string): Filter by created_at >= (ISO 8601)
- `date_to` (string): Filter by created_at <= (ISO 8601)
- `page` (integer): Page number
- `per_page` (integer): Items per page

Example response:

```json
{
  "data": [
    {
      "id": "uuid",
      "transaction_id": "uuid",
      "status": "PAID",
      "amount": 500000,
      "payment_method": "bank_transfer",
      "paid_at": "2026-02-10T10:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false
  }
}
```

### GET /payments/:id

Retrieve a single payment transaction.

- Full path: `/api/v1/payments/:id`
- Required scope: `payments:read`

Example response:

```json
{
  "data": {
    "id": "uuid",
    "transaction_id": "uuid",
    "status": "PAID",
    "amount": 500000,
    "gateway_provider": "xendit",
    "payment_method": "bank_transfer"
  }
}
```

## Subscriptions

Recurring billing subscriptions with state management.

### GET /subscriptions

List subscriptions with optional filters.

- Full path: `/api/v1/subscriptions`
- Required scope: `subscriptions:read`

Query parameters:
- `status` (string): Filter: active, paused, cancelled, expired
- `page` (integer): Page number
- `per_page` (integer): Items per page

Example response:

```json
{
  "data": [
    {
      "id": "uuid",
      "service_name": "Cloud Hosting",
      "status": "active",
      "amount": 500000,
      "billing_period": "monthly",
      "next_billing_date": "2026-03-01"
    }
  ],
  "pagination": {
    "has_more": false
  }
}
```

### POST /subscriptions

Create a new subscription.

- Full path: `/api/v1/subscriptions`
- Required scope: `subscriptions:write`

Request body fields:
- `service_name` (string, required): Service name
- `amount` (number, required): Billing amount
- `billing_period` (string, required): Billing cycle
- `start_date` (string): Start date (YYYY-MM-DD)
- `description` (string): Description or notes

Example request body:

```json
{
  "service_name": "Cloud Hosting",
  "amount": 500000,
  "billing_period": "monthly"
}
```

Example response:

```json
{
  "data": {
    "id": "uuid",
    "service_name": "Cloud Hosting",
    "status": "active"
  }
}
```

### GET /subscriptions/:id

Retrieve a single subscription.

- Full path: `/api/v1/subscriptions/:id`
- Required scope: `subscriptions:read`

Example response:

```json
{
  "data": {
    "id": "uuid",
    "service_name": "Cloud Hosting",
    "status": "active",
    "amount": 500000
  }
}
```

### PATCH /subscriptions/:id

Update subscription details.

- Full path: `/api/v1/subscriptions/:id`
- Required scope: `subscriptions:write`

Request body fields:
- `service_name` (string): Service name
- `amount` (number): Amount
- `billing_period` (string): Billing cycle
- `start_date` (string): Start date (YYYY-MM-DD)
- `description` (string): Description or notes

Example request body:

```json
{
  "amount": 600000
}
```

Example response:

```json
{
  "data": {
    "id": "uuid",
    "amount": 600000
  }
}
```

### POST /subscriptions/:id/actions/:action

Execute a state transition. Actions: activate, pause, resume, cancel.

- Full path: `/api/v1/subscriptions/:id/actions/:action`
- Required scope: `subscriptions:write`

Request body fields:
- `expected_version` (integer): Optimistic locking version

Example request body:

```json
{
  "expected_version": 1
}
```

Example response:

```json
{
  "data": {
    "success": true,
    "subscription_id": "uuid",
    "status": "paused"
  }
}
```

## Webhooks

Manage outbound webhook subscriptions to receive real-time event notifications.

### GET /webhooks

List all webhook subscriptions for your workspace.

- Full path: `/api/v1/webhooks`
- Required scope: `webhooks:read`

Example response:

```json
{
  "data": [
    {
      "id": "uuid",
      "url": "https://example.com/webhook",
      "event_types": [
        "transaction.paid"
      ],
      "is_active": true,
      "consecutive_failures": 0,
      "last_delivery_at": "2026-02-10T10:00:00Z"
    }
  ]
}
```

### POST /webhooks

Create a new webhook subscription. A signing secret will be auto-generated.

- Full path: `/api/v1/webhooks`
- Required scope: `webhooks:write`

Request body fields:
- `url` (string, required): HTTPS webhook endpoint URL
- `event_types` (array, required): Event types to subscribe to (use ["*"] for all)
- `description` (string): Human-readable description

Example request body:

```json
{
  "url": "https://example.com/webhook",
  "event_types": [
    "transaction.created",
    "transaction.paid"
  ],
  "description": "ERP Integration"
}
```

Example response:

```json
{
  "data": {
    "id": "uuid",
    "url": "https://example.com/webhook",
    "signing_secret": "whsec_abc123...",
    "event_types": [
      "transaction.created",
      "transaction.paid"
    ]
  }
}
```

### GET /webhooks/:id

Retrieve a single webhook subscription.

- Full path: `/api/v1/webhooks/:id`
- Required scope: `webhooks:read`

Example response:

```json
{
  "data": {
    "id": "uuid",
    "url": "https://example.com/webhook",
    "is_active": true
  }
}
```

### PATCH /webhooks/:id

Update webhook URL, event types, or status.

- Full path: `/api/v1/webhooks/:id`
- Required scope: `webhooks:write`

Request body fields:
- `url` (string): Webhook URL
- `event_types` (array): Event types
- `is_active` (boolean): Active status

Example request body:

```json
{
  "event_types": [
    "*"
  ],
  "is_active": true
}
```

Example response:

```json
{
  "data": {
    "id": "uuid",
    "event_types": [
      "*"
    ],
    "is_active": true
  }
}
```

### DELETE /webhooks/:id

Permanently delete a webhook subscription.

- Full path: `/api/v1/webhooks/:id`
- Required scope: `webhooks:write`

### GET /webhooks/:id/deliveries

View delivery attempts and their results for a webhook subscription.

- Full path: `/api/v1/webhooks/:id/deliveries`
- Required scope: `webhooks:read`

Query parameters:
- `status` (string): Filter: success, failed, dead_letter
- `page` (integer): Page number
- `per_page` (integer): Items per page

Example response:

```json
{
  "data": [
    {
      "id": "uuid",
      "event_type": "transaction.paid",
      "status": "success",
      "response_status": 200,
      "response_time_ms": 245,
      "attempt_number": 1,
      "created_at": "2026-02-10T10:00:00Z"
    }
  ]
}
```

### POST /webhooks/:id/test

Send a synthetic test event to verify your webhook endpoint.

- Full path: `/api/v1/webhooks/:id/test`
- Required scope: `webhooks:write`

Example response:

```json
{
  "data": {
    "success": true,
    "status_code": 200,
    "response_time_ms": 150
  }
}
```

## Settings

Workspace document, email template, invoice delivery, and invoice style settings for external invoice apps. Document number formats are intentionally excluded.

### GET /settings/document-email

Read the workspace settings used by Settings > Dokumen & Email, including invoice delivery defaults, email templates, invoice style, and company display data.

- Full path: `/api/v1/settings/document-email`
- Required scope: `settings:read`

Example response:

```json
{
  "data": {
    "workspace_id": "uuid",
    "invoice_send_settings": {
      "default_delivery_channels": [
        "email",
        "whatsapp",
        "link"
      ],
      "default_include_payment_link": true,
      "default_link_visibility": "public",
      "default_link_expiry": "30d"
    },
    "email_templates": [
      {
        "template_type": "invoice_generated",
        "subject_template": "Invoice {{invoice_number}} dari {{company_name}}",
        "is_active": true,
        "version": 2
      }
    ],
    "invoice_style": {
      "template_id": "zahir-modern",
      "invoice_label": "INVOICE",
      "invoice_text_color": "#111827"
    },
    "company": {
      "company_name": "PT Contoh Jaya",
      "company_logo": "https://cdn.example.com/logo.png"
    },
    "blocked_fields": [
      "document_number_formats"
    ]
  }
}
```

### PUT /settings/document-email

Update invoice delivery defaults, email templates, company display data, promo banner settings, and invoice style. Format Nomor Dokumen is ignored and must remain managed by TagihanAI.

- Full path: `/api/v1/settings/document-email`
- Required scope: `settings:write`

Request body fields:
- `invoice_send_settings` (object): Default delivery channel, payment link, logo visibility, tax, and notes settings
- `email_templates` (array): Email templates keyed by template_type with subject_template, body_template, variables, configuration, and is_active
- `invoice_style` (object): Invoice label, colors, fonts, display toggles, and footer text
- `company` (object): Company logo, email, website, address, and optional company_name
- `promo_banner` (object): Optional Living Invoice promo banner settings

Example request body:

```json
{
  "invoice_send_settings": {
    "default_delivery_channels": [
      "email",
      "whatsapp",
      "link"
    ],
    "default_include_payment_link": true,
    "default_link_visibility": "public",
    "default_link_expiry": "30d",
    "default_show_company_logo_on_living": true,
    "default_show_company_logo_on_email": true
  },
  "email_templates": [
    {
      "template_type": "invoice_generated",
      "template_name": "Invoice dari Zahir",
      "subject_template": "Invoice {{invoice_number}} dari {{company_name}}",
      "body_template": "Halo {{customer_name}}, silakan buka {{invoice_link}}.",
      "is_active": true,
      "configuration": {
        "channels": [
          "email"
        ],
        "include_payment_link": true,
        "link_expiry": "30d"
      }
    }
  ],
  "invoice_style": {
    "template_id": "zahir-modern",
    "invoice_label": "INVOICE",
    "invoice_text_color": "#111827",
    "table_header_background_color": "#0F766E",
    "table_background_color": "#F8FAFC",
    "total_background_color": "#0F766E",
    "invoice_title_font": "Inter",
    "header_font": "Inter",
    "body_font": "Inter",
    "show_company_logo": true,
    "show_company_address": true,
    "show_customer_address": true,
    "show_footer": true,
    "footer_text": "Terima kasih atas kepercayaan Anda."
  },
  "company": {
    "company_logo": "https://zahir-assets.example.com/company-logo.png",
    "company_email": "billing@company.co.id",
    "company_website": "https://company.co.id",
    "company_address": "Jl. Contoh No. 10, Jakarta"
  }
}
```

Example response:

```json
{
  "data": {
    "workspace_id": "uuid",
    "updated": {
      "invoice_settings": true,
      "email_templates": 1,
      "invoice_style": true,
      "company": true
    },
    "blocked_fields": [
      "document_number_formats"
    ],
    "updated_at": "2026-06-30T05:30:00.000Z"
  }
}
```

## Public

Public endpoints that do not require authentication. Includes free invoice creation and developer app registration for administrator review.

### POST /public/create-invoice

Create an invoice without authentication. Automatically creates a user account and workspace for the given email. If the email already exists, returns a login redirect instead.

- Full path: `/api/v1/public/create-invoice`
- Required scope: `public`

Request body fields:
- `email` (string, required): Email address for account creation
- `company_name` (string): Sender company name (optional)
- `customer_name` (string, required): Customer/recipient name
- `items` (array, required): Line items with description, quantity, unit_price
- `due_date` (string): Due date in YYYY-MM-DD format (optional)
- `notes` (string): Additional notes (optional)
- `description` (string): Invoice description (optional)

Example request body:

```json
{
  "email": "user@example.com",
  "company_name": "PT Contoh Jaya",
  "customer_name": "PT Pelanggan Utama",
  "items": [
    {
      "description": "Jasa Konsultasi IT",
      "quantity": 1,
      "unit_price": 5000000
    },
    {
      "description": "Setup Server",
      "quantity": 2,
      "unit_price": 1500000
    }
  ],
  "due_date": "2026-04-01",
  "notes": "Pembayaran via transfer bank"
}
```

Example response:

```json
{
  "success": true,
  "invoice_id": "uuid",
  "document_number": "INV-0001",
  "public_url": "/invoice/public/abc123...",
  "workspace_id": "uuid",
  "total": 8000000
}
```

### POST /api/developer/app-requests

Submit a third-party application for TagihanAI administrator review. Humans should use /developer#register for the guided signup form; this endpoint is available for automated partner workflows. Approved apps can receive sandbox and live partner credentials.

- Full path: `/api/developer/app-requests`
- Required scope: `public`

Request body fields:
- `app_name` (string, required): Application display name
- `source_slug` (string, required): Unique source slug, lowercase letters/numbers/dash/underscore
- `company_name` (string, required): Legal or public company name
- `company_website` (string): Company website URL
- `contact_name` (string): Technical or business contact name
- `contact_email` (string, required): Business email for review follow-up
- `technical_contact_email` (string): Alias accepted for contact_email
- `business_contact_email` (string): Alias accepted for contact_email
- `app_category` (string, required): Application category
- `callback_domains` (string or array, required): HTTPS callback or webhook domain(s), comma-separated or array
- `integration_use_case` (string, required): Integration use case and expected data flow
- `requested_scopes` (array): Optional requested scopes. Defaults include transaction, payment, webhook, and document-email settings scopes.
- `requested_webhook_events` (array): Optional webhook events requested for approval.

Example request body:

```json
{
  "app_name": "Zahir Smart Invoice",
  "source_slug": "zahir",
  "company_name": "PT Zahir Internasional",
  "company_website": "https://zahironline.com",
  "contact_name": "Zahir Technical Team",
  "technical_contact_email": "developer@zahironline.com",
  "business_contact_email": "partnerships@zahironline.com",
  "app_category": "ERP or accounting",
  "callback_domains": "https://app.zahironline.com",
  "integration_use_case": "Push sales invoices to TagihanAI, generate Living Invoice URLs, sync manual payments, and receive payment webhooks."
}
```

Example response:

```json
{
  "success": true,
  "data": {
    "request": {
      "id": "uuid",
      "app_name": "Zahir Smart Invoice",
      "source_slug": "zahir",
      "status": "submitted",
      "submitted_at": "2026-06-30T10:00:00Z"
    }
  },
  "message": "Registrasi aplikasi diterima untuk review administrator."
}
```

## Webhook Events

- `transaction.created` (Transactions): A new transaction was created
- `transaction.updated` (Transactions): A transaction was updated
- `transaction.submitted` (Transactions): A transaction was submitted for approval
- `transaction.approved` (Transactions): A transaction was approved
- `transaction.rejected` (Transactions): A transaction was rejected
- `transaction.sent` (Transactions): A transaction was sent to the counterparty
- `transaction.viewed` (Transactions): A living invoice was viewed by the recipient for the first time
- `transaction.revised` (Transactions): A sent transaction was revised by the source system
- `transaction.cancelled` (Transactions): A transaction was cancelled or voided
- `transaction.partially_paid` (Transactions): A transaction received a partial payment
- `transaction.paid` (Transactions): A transaction was marked as paid
- `transaction.overdue` (Transactions): A transaction became overdue
- `transaction.deleted` (Transactions): A transaction was deleted
- `payment.created` (Payments): A payment transaction was initiated
- `payment.recorded` (Payments): A manual payment was recorded by an external source app
- `payment.completed` (Payments): A payment was completed successfully
- `payment.failed` (Payments): A payment attempt failed
- `payment.expired` (Payments): A payment request expired
- `subscription.activated` (Subscriptions): A subscription was activated
- `subscription.paused` (Subscriptions): A subscription was paused
- `subscription.resumed` (Subscriptions): A subscription was resumed
- `subscription.cancelled` (Subscriptions): A subscription was cancelled
- `party.created` (Parties): A customer or vendor was created
- `party.updated` (Parties): A customer or vendor was updated
