> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect AI agents to Odoo

The Odoo connector lets your AI agent act in each user's Odoo account. Each user connects their own Odoo access token once, and Scalekit sends it with every call, so your agent never handles credentials. It comes with 23 tools.

**Authentication:** Bearer Token
**Categories:** Accounting & Finance, CRM & Sales
**Tools:** 23: 13 read, 9 write, 1 destructive
**Users sign in with:** Access token
**Built by:** Scalekit connector
**Try it:** [Playground in the Scalekit dashboard](https://app.scalekit.com/ws/signup?sk_intent=playground&provider=ODOO)

## Setup

1. ### Install the SDK

   **Node.js**

   ```bash
   npm install @scalekit-sdk/node dotenv
   ```

   **Python**

   ```bash
   pip install scalekit-sdk-python python-dotenv
   ```

2. ### Set your credentials

   Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**.

   ```sh title=".env"
   SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
   SCALEKIT_CLIENT_ID=<your-client-id>
   SCALEKIT_CLIENT_SECRET=<your-client-secret>
   ```

3. ### Create the Odoo connection

   In **AgentKit > Connections**, create an Odoo connection. The name you give it is the `connection_name` your code passes. See [Configure connections](/agentkit/connections/).

4. ### Authorize a user and make your first call

   **Node.js** (`quickstart.mts`)

   ```typescript
   import { ScalekitClient } from '@scalekit-sdk/node'
   import 'dotenv/config'
   import { createInterface } from 'node:readline/promises'

   const scalekit = new ScalekitClient(
     process.env.SCALEKIT_ENVIRONMENT_URL,
     process.env.SCALEKIT_CLIENT_ID,
     process.env.SCALEKIT_CLIENT_SECRET,
   )
   const actions = scalekit.actions

   const connector = 'odoo'
   const identifier = 'user_123'

   // Generate an authorization link for the user
   const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
   console.log('Authorize Odoo:', link)
   const rl = createInterface({ input: process.stdin, output: process.stdout })
   await rl.question('Press Enter after authorizing...')
   rl.close()

   // Make your first call
   const result = await actions.executeTool({
     connector,
     identifier,
     toolName: 'odoo_get_user_context',
     toolInput: {},
   })
   console.log(result)
   ```

   ```bash
   npx tsx quickstart.mts
   ```

   **Python** (`quickstart.py`)

   ```python
   import os
   from scalekit import ScalekitClient
   from dotenv import load_dotenv
   load_dotenv()

   scalekit_client = ScalekitClient(
       env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
       client_id=os.getenv("SCALEKIT_CLIENT_ID"),
       client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
   )
   actions = scalekit_client.actions

   connection_name = "odoo"
   identifier = "user_123"

   # Generate an authorization link for the user
   link_response = actions.get_authorization_link(
       connection_name=connection_name,
       identifier=identifier,
   )
   print("Authorize Odoo:", link_response.link)
   input("Press Enter after authorizing...")

   # Make your first call
   result = actions.execute_tool(
       tool_input={},
       tool_name="odoo_get_user_context",
       connection_name=connection_name,
       identifier=identifier,
   )
   print(result)
   ```

   ```bash
   python quickstart.py
   ```

   Each user opens the link once and enters their Odoo credentials there. See [Authorize a user](/agentkit/tools/authorize/) for the full flow and statuses.

## Tools

Pass the exact name to `execute_tool`, with an input like each tool's example. To ask for a tool that's missing, use the [request form](https://scalekitsupport.portal.usepylon.com/forms/request-a-connector-tool).

**Node.js**

```typescript
const result = await actions.executeTool({
  toolName: 'odoo_count_records',
  toolInput: {
    model: '<model>',
  },
  connector: 'odoo',
  identifier: 'user_123',
})
```

**Python**

```python
result = actions.execute_tool(
    tool_name="odoo_count_records",
    tool_input={
        "model": "<model>",
    },
    connection_name="odoo",
    identifier="user_123",
)
```

`result.data` is the app's response as JSON, and `result.execution_id` (`executionId` in Node.js) is the ID of the call.

### `odoo_count_records`

Count Records · Read-only

Count the records of an Odoo model that match a domain filter.
Returns a single integer.
Use this to size a result set before paging through odoo_search_read_records.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `domain` (`array`, optional): Odoo search domain: a list of [field, operator, value] conditions that are ANDed together, optionally using the prefix operators '|' (or), '&' (and) and '!' (not). Example: [["is_company", "=", true], ["name", "ilike", "deco"]]. Omit or pass [] to match every record.
- `limit` (`integer`, optional): Optional cap on the count; counting stops once this many matches are found, which is faster on very large tables.

Example input: `{"model":"<model>"}`

### `odoo_get_model_fields`

Get Model Fields · Read-only

Describe the fields of an Odoo model: technical name, label, type, whether required or read-only, relation target and selection values.
Returns an object keyed by field name.
Call this before creating or filtering on an unfamiliar model so field names and valid values are correct.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `attributes` (`array`, optional): Optional list of field attributes to return for each field. Useful values are string, type, required, readonly, relation and selection. Omit for all attributes (large for big models).
- `fields` (`array`, optional): Optional list of field names to describe. Omit to describe every field of the model.

Example input: `{"model":"<model>"}`

### `odoo_get_user_context`

Get Current User Context · Read-only

Get the context of the user that owns the API key, including their user ID (uid), language and timezone.
Returns an object such as {"lang": "en_US", "tz": "Europe/Brussels", "uid": 2}.
Use this to learn the authenticated user's ID, for example before assigning records to yourself.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs: none.

Example input: `{}`

### `odoo_list_contacts`

List Contacts · Read-only

List contacts (customers, vendors, people and companies) from Odoo, optionally filtered by text or type.
Returns an array of partner records with id, name, email, phone, company flag and address fields.
Use this to browse or find contacts. Use odoo_name_search_records when only an id and name are needed, and odoo_create_contact to add one.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'email', 'phone', 'is_company', 'city', 'country_id', 'parent_id']. Use odoo_get_model_fields to discover other field names.
- `is_company` (`boolean`, optional): Set true to return only companies, false to return only individuals. Omit for both.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `search` (`string`, optional): Text to match case-insensitively against name, email or phone. Omit for no text filter.

Example input: `{}`

### `odoo_list_employees`

List Employees · Read-only

List employees from Odoo, optionally filtered by text.
Returns an array of employee records with id, name, job title, work email, work phone and department.
Requires the Employees app.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'job_title', 'work_email', 'work_phone', 'department_id', 'parent_id']. Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `search` (`string`, optional): Text to match case-insensitively against employee name or work email. Omit for no text filter.

Example input: `{}`

### `odoo_list_invoices`

List Invoices and Bills · Read-only

List customer invoices, vendor bills and credit notes from Odoo, optionally filtered by type, state, payment state or partner.
Returns an array of journal entry records with id, number, partner, type, state, payment state, dates and total.
Requires the Invoicing or Accounting app. Journal entries that are not invoices are excluded unless move_type is set to entry.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'partner_id', 'move_type', 'state', 'payment_state', 'invoice_date', 'invoice_date_due', 'amount_total', 'currency_id']. Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `move_type` (`string`, optional): Kind of document: out_invoice is a customer invoice, in_invoice a vendor bill, out_refund a customer credit note, in_refund a vendor credit note. Defaults to customer and vendor invoices and bills. One of: `out_invoice`, `in_invoice`, `out_refund`, `in_refund`, `out_receipt`, `in_receipt`, `entry`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `partner_id` (`integer`, optional): Only return documents for this partner (res.partner ID). Find IDs with odoo_list_contacts.
- `payment_state` (`string`, optional): Filter by payment state: not_paid, in_payment, paid, partial or reversed. Omit for all. One of: `not_paid`, `in_payment`, `paid`, `partial`, `reversed`.
- `state` (`string`, optional): Filter by posting state: draft, posted or cancel. Omit for all. One of: `draft`, `posted`, `cancel`.

Example input: `{}`

### `odoo_list_leads`

List CRM Leads and Opportunities · Read-only

List CRM leads and opportunities from Odoo, optionally filtered by text, type or probability.
Returns an array of lead records with id, name, contact, email, expected revenue, probability, stage and salesperson.
Requires the CRM app. Use this to browse the pipeline and odoo_create_lead to add one.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'partner_id', 'email_from', 'phone', 'expected_revenue', 'probability', 'stage_id', 'user_id', 'type']. Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `search` (`string`, optional): Text to match case-insensitively against lead name, contact name or email. Omit for no text filter.
- `type` (`string`, optional): Restrict to raw leads or to opportunities. Omit for both. One of: `lead`, `opportunity`.

Example input: `{}`

### `odoo_list_products`

List Products · Read-only

List products and variants from Odoo, optionally filtered by text or sale availability.
Returns an array of product records with id, name, internal reference, barcode, sales price and type.
Use this to find product IDs for sales order or invoice lines.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'default_code', 'barcode', 'list_price', 'standard_price', 'type']. Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `sale_ok` (`boolean`, optional): Set true to return only products that can be sold, false for the rest. Omit for both.
- `search` (`string`, optional): Text to match case-insensitively against product name, internal reference or barcode. Omit for no text filter.

Example input: `{}`

### `odoo_list_sale_orders`

List Sales Orders · Read-only

List quotations and sales orders from Odoo, optionally filtered by text, state or customer.
Returns an array of order records with id, reference, customer, state, order date and total amount.
Requires the Sales app. Use odoo_confirm_sale_order to confirm a quotation.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'partner_id', 'state', 'date_order', 'amount_total', 'user_id']. Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `partner_id` (`integer`, optional): Only return orders for this customer (res.partner ID). Find IDs with odoo_list_contacts.
- `search` (`string`, optional): Text to match case-insensitively against order reference or customer reference. Omit for no text filter.
- `state` (`string`, optional): Filter by order state: draft is a quotation, sent is a quotation sent, sale is a confirmed order, cancel is cancelled. Omit for all states. One of: `draft`, `sent`, `sale`, `cancel`.

Example input: `{}`

### `odoo_list_tasks`

List Project Tasks · Read-only

List project tasks from Odoo, optionally filtered by text, project or assignee.
Returns an array of task records with id, name, project, assignees, stage, deadline and priority.
Requires the Project app. Use odoo_create_task to add a task.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `domain` (`array`, optional): Optional extra Odoo domain ANDed with the other filters, for conditions not covered by the named filters. Example: [["create_date", ">=", "2025-01-01"]].
- `fields` (`array`, optional): List of field names to return per record. Defaults to ['name', 'project_id', 'user_ids', 'stage_id', 'date_deadline', 'priority', 'state']. Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.
- `project_id` (`integer`, optional): Only return tasks of this project (project.project ID). Find IDs with odoo_search_read_records on project.project.
- `search` (`string`, optional): Text to match case-insensitively against task name. Omit for no text filter.
- `user_id` (`integer`, optional): Only return tasks assigned to this user (res.users ID). Use odoo_get_user_context for your own ID.

Example input: `{}`

### `odoo_name_search_records`

Search Records by Name · Read-only

Find records of an Odoo model whose display name matches a text pattern, optionally narrowed by a domain.
Returns a JSON array of [id, display name] pairs.
Use this to resolve a name such as a customer, product or stage to its ID before linking it in a create or update call.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `name` (`string`, required): Text to match against the record display name. An empty string matches any name.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `domain` (`array`, optional): Odoo search domain: a list of [field, operator, value] conditions that are ANDed together, optionally using the prefix operators '|' (or), '&' (and) and '!' (not). Example: [["is_company", "=", true], ["name", "ilike", "deco"]]. Omit or pass [] to match every record.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 100 to keep responses small; raise it or page with offset for more. Default: `100`.
- `operator` (`string`, optional): Comparison operator used for the name match. ilike is case-insensitive substring match; = is exact match. One of: `ilike`, `like`, `=`, `=ilike`, `not ilike`. Default: `ilike`.

Example input: `{"model":"<model>","name":"<name>"}`

### `odoo_read_records`

Read Records by ID · Read-only

Read specific records of an Odoo model by their IDs.
Returns a JSON array with one object per ID, containing the requested fields; many2one fields come back as [id, display name] pairs.
Use this when you already have IDs. Use odoo_search_read_records to find records by filter.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `ids` (`array`, required): Integer IDs of the records to act on. Get IDs from a search or list tool.
- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `fields` (`array`, optional): List of field names to return per record. Defaults to every field of the model (can be large; always pass fields for a lean response). Use odoo_get_model_fields to discover other field names.

Example input: `{"ids":[],"model":"<model>"}`

### `odoo_search_read_records`

Search and Read Records · Read-only

Search any Odoo model with a domain filter and return the matching records with the requested fields.
Returns a JSON array of record objects; many2one fields come back as [id, display name] pairs.
Use this for custom models or complex filters. Use odoo_list_contacts, odoo_list_leads, odoo_list_sale_orders, odoo_list_invoices, odoo_list_products, odoo_list_tasks or odoo_list_employees for those common models, and odoo_count_records when only a total is needed.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `domain` (`array`, optional): Odoo search domain: a list of [field, operator, value] conditions that are ANDed together, optionally using the prefix operators '|' (or), '&' (and) and '!' (not). Example: [["is_company", "=", true], ["name", "ilike", "deco"]]. Omit or pass [] to match every record.
- `fields` (`array`, optional): List of field names to return per record. Defaults to every field of the model (can be large; always pass fields for a lean response). Use odoo_get_model_fields to discover other field names.
- `limit` (`integer`, optional): Maximum number of records to return. Defaults to 50 to keep responses small; raise it or page with offset for more. Default: `50`.
- `offset` (`integer`, optional): Number of matching records to skip before returning results, used for paging together with limit. Default: `0`.
- `order` (`string`, optional): Sort expression as comma-separated 'field asc|desc' terms, for example 'create_date desc' or 'name asc, id desc'. Omit for Odoo's default model order.

Example input: `{"model":"<model>"}`

### `odoo_archive_records`

Archive Records · Write

Archive records of an Odoo model so they are hidden from default searches without deleting them.
Returns true on success (some models return an action object instead).
Use this instead of odoo_delete_records for reversible removal. Only models with an active field support it.

Read only: no. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `ids` (`array`, required): Integer IDs of the records to archive to act on. Get IDs from a search or list tool.
- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.

Example input: `{"ids":[],"model":"<model>"}`

### `odoo_call_model_method`

Call Model Method · Write

Call any public ORM method on an Odoo model, optionally on specific record IDs, with named arguments.
Returns whatever the method returns: true, a value, a list, or an action object.
Use this for workflow actions without a dedicated tool, such as action_confirm, action_post or action_cancel. Prefer the dedicated tools for search, read, create, update and delete.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `method` (`string`, required): Name of the public method to call, without a leading underscore, for example action_confirm, action_post, action_cancel or copy.
- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `arguments` (`object`, optional): Object of named arguments passed to the method. JSON-2 only supports named arguments, so use the exact parameter names from the Odoo method signature. Example for copy: {"default": {"name": "Copy"}}.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `ids` (`array`, optional): IDs of the records the method runs on. Leave empty for model-level methods that do not act on specific records.

Example input: `{"method":"<method>","model":"<model>"}`

### `odoo_confirm_sale_order`

Confirm Sales Order · Write

Confirm one or more quotations in Odoo, turning them into sales orders.
Returns true on success.
Requires the Sales app. Use odoo_list_sale_orders with state draft to find quotations.

Read only: no. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `order_ids` (`array`, required): IDs of the quotations (sale.order) to confirm.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.

Example input: `{"order_ids":[]}`

### `odoo_create_contact`

Create Contact · Write

Create a contact (individual or company) in Odoo.
Returns a JSON array containing the ID of the new contact.
Use odoo_list_contacts first to avoid duplicates. Use odoo_create_records for fields not covered here.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `name` (`string`, required): Full name of the person or company.
- `city` (`string`, optional): City name.
- `comment` (`string`, optional): Internal notes about the contact.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `email` (`string`, optional): Email address of the contact.
- `is_company` (`boolean`, optional): Set true to create a company, false for an individual. Default: `false`.
- `parent_id` (`integer`, optional): ID of the company this person works for (res.partner ID).
- `phone` (`string`, optional): Phone number of the contact.
- `street` (`string`, optional): Street address line.
- `vat` (`string`, optional): VAT or tax identification number.
- `website` (`string`, optional): Website URL of the contact.

Example input: `{"name":"<name>"}`

### `odoo_create_lead`

Create CRM Lead or Opportunity · Write

Create a lead or opportunity in the Odoo CRM pipeline.
Returns a JSON array containing the ID of the new record.
Requires the CRM app. Use odoo_list_leads to review the pipeline.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `name` (`string`, required): Title of the lead or opportunity.
- `contact_name` (`string`, optional): Name of the person to contact, used when no customer is linked.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `description` (`string`, optional): Internal notes for the lead.
- `email_from` (`string`, optional): Contact email address for the lead.
- `expected_revenue` (`number`, optional): Expected revenue of the opportunity in the company currency.
- `partner_id` (`integer`, optional): ID of an existing contact to link (res.partner ID). Find IDs with odoo_list_contacts.
- `phone` (`string`, optional): Contact phone number for the lead.
- `type` (`string`, optional): lead creates a raw lead, opportunity creates an opportunity in the pipeline. Odoo applies the database setting when omitted. One of: `lead`, `opportunity`.
- `user_id` (`integer`, optional): ID of the salesperson (res.users ID). Use odoo_get_user_context for your own ID.

Example input: `{"name":"<name>"}`

### `odoo_create_records`

Create Records · Write

Create one or more records in any Odoo model from field-value objects.
Returns a JSON array with the ID of each created record.
Use odoo_create_contact, odoo_create_lead or odoo_create_task for those models. Use odoo_get_model_fields first to learn required fields and valid values.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `records` (`array`, required): Array of objects, one per record to create, each mapping field names to values. Many2one fields take the related record's integer ID; one2many and many2many fields use Odoo command lists such as [[0, 0, {...}]] to create lines and [[6, 0, [ids]]] to set links. Example: [{"name": "Deco Addict", "is_company": true}].
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.

Example input: `{"model":"<model>","records":[{"name":"Deco Addict","is_company":true}]}`

### `odoo_create_task`

Create Project Task · Write

Create a task in an Odoo project.
Returns a JSON array containing the ID of the new task.
Requires the Project app. Use odoo_list_tasks to review existing tasks.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `name` (`string`, required): Title of the task.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `date_deadline` (`string`, optional): Deadline as a date and time in UTC, formatted YYYY-MM-DD HH:MM:SS.
- `description` (`string`, optional): Task description as plain text or HTML.
- `project_id` (`integer`, optional): ID of the project the task belongs to (project.project ID).
- `user_ids` (`array`, optional): IDs of the users to assign (res.users IDs). Use odoo_get_user_context for your own ID.

Example input: `{"name":"<name>"}`

### `odoo_update_contact`

Update Contact · Write

Update fields of an existing contact in Odoo; only the fields you pass are changed.
Returns true on success.
Use odoo_list_contacts to find the contact ID, and odoo_update_records for fields not covered here.

Read only: no. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `contact_id` (`integer`, required): ID of the contact (res.partner) to update.
- `city` (`string`, optional): New city name.
- `comment` (`string`, optional): New internal notes (replaces the existing notes).
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.
- `email` (`string`, optional): New email address.
- `name` (`string`, optional): New full name.
- `phone` (`string`, optional): New phone number.
- `street` (`string`, optional): New street address line.
- `website` (`string`, optional): New website URL.

Example input: `{"contact_id":1}`

### `odoo_update_records`

Update Records · Write

Update the same field values on one or more existing records of an Odoo model.
Returns true on success.
Use odoo_update_contact for a single contact. Use odoo_archive_records to deactivate records instead of deleting them.

Read only: no. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `ids` (`array`, required): Integer IDs of the records to act on. Get IDs from a search or list tool.
- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `values` (`object`, required): Object mapping field names to the new values, applied to every record in ids. Many2one fields take the related record's integer ID; one2many and many2many fields use Odoo command lists such as [[6, 0, [ids]]]. Example: {"phone": "+1 555 0100", "comment": "VIP"}.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.

Example input: `{"ids":[],"model":"<model>","values":{"phone":"+1 555 0100","comment":"VIP"}}`

### `odoo_delete_records`

Delete Records · Destructive

Permanently delete records of an Odoo model by ID.
Returns true on success. Odoo refuses with an error when other records still reference the targets or business rules forbid deletion.
Prefer odoo_archive_records to hide records reversibly; use this only when permanent removal is intended.

Read only: no. Destructive: yes. Idempotent (safe to retry): yes.

Inputs:

- `ids` (`array`, required): Integer IDs of the records to permanently delete to act on. Get IDs from a search or list tool.
- `model` (`string`, required): Technical name of the Odoo model to operate on, with dots (for example res.partner, crm.lead, sale.order, account.move, product.product, project.task, hr.employee). Use odoo_get_model_fields to inspect a model.
- `context` (`object`, optional): Optional context object passed to the ORM, for example {"lang": "fr_FR"} for translated values or {"active_test": false} to include archived records in searches.

Example input: `{"ids":[],"model":"<model>"}`

## Related

Other Accounting & Finance connectors ([all 37](/agentkit/connectors/?category=accounting-finance)):

- [QuickBooks](/agentkit/connectors/quickbooks/): Scalekit connector, OAuth, 117 tools
- [Xero](/agentkit/connectors/xero/): Scalekit connector, OAuth, 94 tools
- [Stripe](/agentkit/connectors/stripe/): Scalekit connector, Access token, 178 tools


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
