Skip to content

Organization Settings API

API endpoints for managing Zoho organization configurations. All endpoints require token_admin authentication.

Authentication

All endpoints require a valid JWT token:

Authorization: Bearer <token>

Organizations

MethodPathDescription
GET/finances/organizationList all Zoho organizations

GET /finances/organization

Returns all Zoho organizations (receivers) with country information.

Response (200):

json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "zoho_organization_id": 861752378,
      "name": "TENDENCYS INNOVATIONS",
      "rfc": "TIN1603038C4",
      "locale_id": 1,
      "country_name": "Mexico"
    }
  ]
}

Taxes

MethodPathDescription
GET/finances/organization/{orgId}/taxesList all taxes for an organization
POST/finances/organization/{orgId}/taxesCreate tax (individual or group)
PUT/finances/organization/taxes/{id}Update tax or group
DELETE/finances/organization/taxes/{id}Soft-delete tax (set inactive)
GET/finances/organization/{orgId}/taxes/sync-previewPreview taxes available from Zoho
POST/finances/organization/{orgId}/taxes/syncBulk import taxes from Zoho

POST /finances/organization/{orgId}/taxes

Creates a new individual tax or tax group. Syncs with Zoho Books API.

Path params: orgId (number) — Zoho organization ID

Request:

json
{
  "label": "ReteRenta (2.5%)",
  "rate": 0.025,
  "tax_code": "002",
  "tax_type": "retained",
  "type": "individual",
  "sort_order": 1
}

For tax groups:

json
{
  "tax_key": "ret_co_honorarios",
  "label": "Honorarios + ReteIVA",
  "tax_type": "retained",
  "type": "group",
  "child_ids": [5, 8]
}

Payload fields:

FieldTypeRequiredDescription
labelstring(100)YesHuman-readable label
ratenumber (0-1)Individual onlyTax rate as decimal
tax_codestring(10)Individual onlyFiscal code
tax_typetransferred | retainedYesTax type
typeindividual | groupNoDefault: individual
child_idsnumber[] (min 2)Group onlyMember tax IDs
sort_ordernumberNoDisplay order

tax_key is auto-generated from label for individual taxes. For groups, it can be provided or auto-generated.

Response (201):

json
{
  "success": true,
  "data": { "id": 15, "zoho_tax_id": "5528319000006718624" }
}

Errors:

CodeDescription
400Invalid payload or missing required fields
409Tax group requires at least 2 synced children
500Zoho API rejected the operation

GET /finances/organization/{orgId}/taxes/sync-preview

Compares Zoho taxes with local database and returns missing taxes.

Internally makes two Zoho API calls:

  1. GET /v3/settings/taxes — standard taxes
  2. GET /v3/settings/taxes?tax_type=income_tax_tds — retention taxes + groups

For groups, fetches individual group details to resolve member taxes.

Response (200):

json
{
  "success": true,
  "data": [
    {
      "tax_id": "6656156000000105081",
      "tax_name": "IVA",
      "tax_percentage": 16,
      "tax_type": "tax",
      "already_synced": false
    }
  ]
}

POST /finances/organization/{orgId}/taxes/sync

Bulk inserts selected taxes from Zoho into local database.

Request:

json
{
  "taxes": [
    {
      "tax_id": "6656156000000105081",
      "tax_name": "IVA",
      "tax_percentage": 16,
      "tax_type": "tax"
    }
  ]
}

Accounts (Chart of Accounts)

MethodPathDescription
GET/finances/organization/{orgId}/accountsList accounts for an organization
POST/finances/organization/{orgId}/accountsCreate account (syncs to Zoho)
PUT/finances/organization/accounts/{id}Update account
PATCH/finances/organization/accounts/{id}/toggle-activeToggle active status
DELETE/finances/organization/accounts/{id}Delete account
GET/finances/organization/{orgId}/accounts/sync-previewPreview accounts from Zoho
POST/finances/organization/{orgId}/accounts/syncBulk import accounts from Zoho

POST /finances/organization/{orgId}/accounts

Creates an account in both Zoho and local database. The account_id is obtained from Zoho's response.

Request:

json
{
  "description": "Office Supplies",
  "account_type": "expense",
  "parent_account_id": null,
  "department_id": 3
}

name is auto-generated by normalizing description (lowercase, no spaces, no accents).

department_id is only valid for parent accounts (ignored for children).

Payload fields:

FieldTypeRequiredDescription
descriptionstring(255)YesHuman-readable name
account_typestringYesZoho account type (expense, income, etc.)
parent_account_idnumber | nullNoParent account ID for child accounts
department_idnumber | nullNoDepartment (parent accounts only)

GET /finances/organization/{orgId}/accounts/sync-preview

Returns a 2-level hierarchical tree of accounts present in Zoho but missing in local DB.

Query params:

ParamTypeRequiredDescription
accountTypestringNoFilter by Zoho account type: Expense, Income, Asset, Liability, Equity, CostOfGoodsSold

Response (200):

json
{
  "success": true,
  "data": [
    {
      "account_id": "5528319000000035001",
      "account_name": "Cost of Goods Sold",
      "account_type": "cost_of_goods_sold",
      "is_parent": true,
      "already_synced": false,
      "children": [
        {
          "account_id": "5528319000000035005",
          "account_name": "Raw Materials",
          "account_type": "cost_of_goods_sold",
          "already_synced": false
        }
      ]
    }
  ]
}

Parents with all children already synced are shown with already_synced: true but include their unsynced children.

POST /finances/organization/{orgId}/accounts/sync

Bulk imports selected accounts. Parents are inserted first, then children with the correct parent_account_id.

Request:

json
{
  "accounts": [
    {
      "account_id": "5528319000000035001",
      "account_name": "Cost of Goods Sold",
      "account_type": "cost_of_goods_sold",
      "department_id": 3,
      "children": [
        {
          "account_id": "5528319000000035005",
          "account_name": "Raw Materials",
          "account_type": "cost_of_goods_sold"
        }
      ]
    }
  ]
}

Banks

MethodPathDescription
GET/finances/organization/{orgId}/banksList banks for an organization
POST/finances/organization/{orgId}/banksCreate bank
PUT/finances/organization/banks/{id}Update bank
PATCH/finances/organization/banks/{id}/toggle-activeToggle active status
GET/finances/organization/{orgId}/banks/sync-previewPreview banks from Zoho
POST/finances/organization/{orgId}/banks/syncBulk import banks from Zoho

POST /finances/organization/{orgId}/banks

Creates a new bank record.

Request:

json
{
  "description": "BBVA Bancomer Principal",
  "account_type": "bank"
}

name is auto-generated from description.

Payload fields:

FieldTypeRequiredDescription
descriptionstring(255)YesHuman-readable bank name
account_typestringYesbank, cash, or credit_card

GET /finances/organization/{orgId}/banks/sync-preview

Returns a flat list of Zoho bank accounts not yet in local DB.

Response (200):

json
{
  "success": true,
  "data": [
    {
      "account_id": "5528319000000070001",
      "account_name": "Petty Cash",
      "account_type": "cash",
      "is_active": true,
      "already_synced": false
    }
  ]
}

POST /finances/organization/{orgId}/banks/sync

Bulk inserts selected bank accounts. name is auto-generated from description (the Zoho account_name).

Request:

json
{
  "banks": [
    {
      "account_id": "5528319000000070001",
      "account_name": "Petty Cash",
      "account_type": "cash"
    }
  ]
}

Common Error Responses

All errors follow the Boom format:

json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Descriptive error message"
}
StatusWhen
400Validation failed (Joi schema)
401Missing or invalid JWT
403Insufficient permissions
404Resource not found
409Business rule conflict (e.g., deactivating parent with active children)
500Zoho API rejected the operation

Envia Admin