90 private $endpoints = [
92 'module' =>
'societe',
93 'path' =>
'/societe/class/api_thirdparties.class.php',
94 'class' =>
'Thirdparties',
95 'label' =>
'third parties (customers, prospects, suppliers)',
98 'description' =>
"Use 'mode' to restrict to a nature of third party instead of filtering on names.",
100 'mode' =>
"Nature filter: 0=all (default), 1=customers/prospects, 2=prospects only, 3=neither customer nor prospect, 4=suppliers.",
101 'category' =>
"Rowid of a third-party category (tag) to restrict the list to."
108 'module' =>
'propal',
109 'path' =>
'/comm/propal/class/api_proposals.class.php',
110 'class' =>
'Proposals',
111 'label' =>
'commercial proposals (quotes / devis)',
115 'thirdparty_ids' =>
"Comma-separated third-party rowids to restrict to (e.g. '1,5')."
122 'module' =>
'ticket',
123 'path' =>
'/ticket/class/api_tickets.class.php',
124 'class' =>
'Tickets',
125 'label' =>
'support tickets',
126 'methods' => [
'index' => [],
'get' => []]
129 'module' =>
'projet',
130 'path' =>
'/projet/class/api_projects.class.php',
131 'class' =>
'Projects',
132 'label' =>
'projects (including opportunities/leads)',
133 'methods' => [
'index' => [],
'get' => []]
136 'module' =>
'projet',
137 'path' =>
'/projet/class/api_tasks.class.php',
139 'label' =>
'project tasks',
140 'methods' => [
'index' => [],
'get' => []]
143 'module' =>
'agenda',
144 'path' =>
'/comm/action/class/api_agendaevents.class.php',
145 'class' =>
'AgendaEvents',
146 'label' =>
'agenda / calendar events (meetings, calls)',
147 'methods' => [
'index' => [],
'get' => []]
150 'module' =>
'ficheinter',
151 'path' =>
'/fichinter/class/api_interventions.class.php',
152 'class' =>
'Interventions',
153 'label' =>
'field service interventions',
154 'methods' => [
'index' => [],
'get' => []]
157 'module' =>
'contrat',
158 'path' =>
'/contrat/class/api_contracts.class.php',
159 'class' =>
'Contracts',
160 'label' =>
'contracts (recurring services)',
161 'methods' => [
'index' => [],
'get' => []]
164 'module' =>
'adherent',
165 'path' =>
'/adherents/class/api_members.class.php',
166 'class' =>
'Members',
167 'label' =>
'foundation/association members',
168 'methods' => [
'index' => [],
'get' => []]
171 'module' =>
'adherent',
172 'path' =>
'/adherents/class/api_subscriptions.class.php',
173 'class' =>
'Subscriptions',
174 'label' =>
'member subscriptions',
175 'methods' => [
'index' => [],
'get' => []]
177 'stockmovements' => [
179 'path' =>
'/product/stock/class/api_stockmovements.class.php',
180 'class' =>
'StockMovements',
181 'label' =>
'stock movements (in/out/transfer history)',
184 'description' =>
"History of physical stock changes; each movement carries product, warehouse, qty (signed) and date."
191 'path' =>
'/product/stock/class/api_warehouses.class.php',
192 'class' =>
'Warehouses',
193 'label' =>
'warehouses',
194 'methods' => [
'index' => [],
'get' => []]
196 'expensereports' => [
197 'module' =>
'expensereport',
198 'path' =>
'/expensereport/class/api_expensereports.class.php',
199 'class' =>
'ExpenseReports',
200 'label' =>
'employee expense reports (notes de frais)',
201 'methods' => [
'index' => [],
'get' => []]
204 'module' =>
'product',
205 'path' =>
'/product/class/api_products.class.php',
206 'class' =>
'Products',
207 'label' =>
'products and services catalog',
212 'suffix' =>
'attributes_list',
213 'description' =>
"Variant attributes (e.g. Size, Color) defined in the catalog."
216 'suffix' =>
'variants_list',
217 'description' =>
"Variants of one parent product.",
218 'params' => [
'id' =>
'Rowid of the PARENT product.']
234 private $commonParamDocs = [
235 'sortfield' =>
"Field to sort on, prefixed with 't.' (e.g. 't.rowid', 't.ref', 't.datec').",
236 'sortorder' =>
"Sort direction: 'ASC' or 'DESC'.",
237 'limit' =>
"Maximum number of records to return.",
238 'page' =>
"Zero-based page index for pagination.",
239 'sqlfilters' =>
"Universal search filter. Example: \"(t.ref:like:'PR%') and (t.datec:>=:'2026-01-01')\". Field names are prefixed with 't.'; operators: =, !=, <, <=, >, >=, like, is; combine clauses with 'and'/'or' and parentheses.",
240 'properties' =>
"Comma-separated list of properties to include in the response, to reduce its size (e.g. 'id,ref,label').",
241 'id' =>
"Rowid (numeric technical id) of the record."
255 if ($conf !==
null) {
270 require_once DOL_DOCUMENT_ROOT .
'/includes/restler/framework/Luracast/Restler/AutoLoader.php';
271 $loader = Luracast\Restler\AutoLoader::instance();
272 spl_autoload_register($loader);
273 require_once DOL_DOCUMENT_ROOT .
'/api/class/api.class.php';
274 require_once DOL_DOCUMENT_ROOT .
'/api/class/api_access.class.php';
287 if ($this->defs !==
null) {
295 foreach ($this->endpoints as $key => $ep) {
299 $file = DOL_DOCUMENT_ROOT . $ep[
'path'];
300 if (!is_readable($file)) {
304 if (!class_exists($ep[
'class'])) {
311 foreach ($ep[
'methods'] as $method => $meta) {
312 if (!method_exists($ep[
'class'], $method)) {
315 $suffix = $meta[
'suffix'] ?? ($method ===
'index' ?
'list' : strtolower($method));
316 $toolname =
'api_' . $key .
'_' . $suffix;
319 $this->defs[] = $def;
320 $this->routes[$toolname] = [$key, $method];
341 private function buildToolDefinition(array $ep,
string $key,
string $method,
string $toolname, array $meta = [])
344 $rm =
new ReflectionMethod($ep[
'class'], $method);
345 }
catch (ReflectionException $e) {
349 $doc = (
string) $rm->getDocComment();
354 if (preg_match(
'/\*\s+([^@\s\/*][^\n]*)/', $doc, $m)) {
355 $summary = trim($m[1]);
360 if (preg_match_all(
'/@param\s+(\S+)\s+\$(\w+)\s+([^\n]*)/', $doc, $mm, PREG_SET_ORDER)) {
361 foreach ($mm as $pm) {
362 $paramDocs[$pm[2]] = [
'type' => $pm[1],
'desc' => trim($pm[3])];
368 foreach ($rm->getParameters() as $p) {
369 $pname = $p->getName();
370 $ptype = isset($paramDocs[$pname]) ? $this->
docTypeToJson($paramDocs[$pname][
'type']) :
'string';
373 if (isset($meta[
'params'][$pname])) {
374 $pdesc = $meta[
'params'][$pname];
375 } elseif (!empty($paramDocs[$pname][
'desc'])) {
376 $pdesc = $paramDocs[$pname][
'desc'];
378 $pdesc = $this->commonParamDocs[$pname] ??
'';
382 'description' => $pdesc
384 if ($p->isOptional()) {
386 $prop[
'default'] = $p->getDefaultValue();
387 }
catch (ReflectionException $e) {
391 $required[] = $pname;
393 $properties[$pname] = $prop;
396 if ($method ===
'index') {
397 $verb =
'List / search';
398 } elseif ($method ===
'get') {
399 $verb =
'Get one record of';
403 $schema = [
'type' =>
'object',
'properties' => $properties];
405 $schema[
'required'] = $required;
408 $description = $verb .
' ' . $ep[
'label'] .
' through the Dolibarr REST API (auto-generated tool). ' . $summary;
409 if (!empty($meta[
'description'])) {
410 $description = rtrim($description) .
' ' . $meta[
'description'];
415 'description' => $description,
416 'inputSchema' => $schema
428 $t = strtolower(trim(explode(
'|', $type)[0]));
429 if (in_array($t, [
'int',
'integer'],
true)) {
432 if (in_array($t, [
'float',
'double'],
true)) {
435 if ($t ===
'bool' || $t ===
'boolean') {
448 return [
'thirdparty',
'commercial',
'billing',
'stock',
'reporting'];
459 public function execute(
string $name, array $args)
462 return [
"error" =>
"API bridge is disabled (AI_MCP_API_BRIDGE not set)."];
466 if (empty($this->routes[$name])) {
467 return [
"error" =>
"Tool function '$name' not found."];
469 list($key, $method) = $this->routes[$name];
470 $ep = $this->endpoints[$key];
477 DolibarrApiAccess::$user = $this->user;
478 $GLOBALS[
'user'] = $this->user;
480 require_once DOL_DOCUMENT_ROOT . $ep[
'path'];
481 $api =
new $ep[
'class']();
484 $rm =
new ReflectionMethod($ep[
'class'], $method);
486 foreach ($rm->getParameters() as $p) {
487 $pname = $p->getName();
488 if (array_key_exists($pname, $args)) {
489 $callArgs[] = $args[$pname];
490 } elseif ($p->isOptional()) {
491 $callArgs[] = $p->getDefaultValue();
493 return [
"error" =>
"Missing required parameter '$pname'."];
498 $result = call_user_func_array([$api, $method], $callArgs);
500 return json_decode(json_encode($result),
true);
501 }
catch (Throwable $e) {
502 $code = (int) $e->getCode();
504 "error" => $e->getMessage(),
505 "http_status" => ($code > 0 ? $code : 500)
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
isModEnabled($module)
Is Dolibarr module enabled.
conf($dolibarr_main_document_root, $realpathconf=null)
Load conf file (file must exists)
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
$conf db user
Active Directory does not allow anonymous connections.