Servidores MCP
- Qué es MCP y cómo conecta asistentes de IA de programación con AutoTalk
- Cómo configurar MCP en Codex y Claude Code
- Qué herramientas están disponibles y cómo usarlas
AutoTalk expone un servidor MCP (Model Context Protocol) que da a los asistentes de IA de programación acceso directo a tus schemas Dynadata, documentos de ejemplo, operaciones CRUD y pruebas de Workflow. Herramientas como Codex y Claude Code pueden leer tus modelos de datos y construir agentes/workflows sin copiar y pegar schemas.
Requisitos previos
Necesitas un token de API para autenticarte con el servidor MCP. Consulta Tokens de API para saber cómo crear uno.
El endpoint MCP es:
https://mcp.autotalk.io/v1
El host público de MCP usa el mismo modelo de autenticación con clave de API que el resto de /v1/. Los clientes MCP externos como Codex y Claude Code deben usar x-api-key como se muestra abajo.
Authorization: Bearer no está soportado — la clave debe enviarse en el header x-api-key. Usuarios de Codex: usen el formato env_http_headers mostrado abajo, no bearer_token_env_var. Un header con nombre equivocado devuelve un 401 unauthorized opaco; si recibes 401 con una clave válida, revisa primero el nombre del header.
Configuración de Codex
Inicia Codex con el servidor MCP de AutoTalk configurado para esta sesión:
AUTOTALK_API_KEY="sk-YOUR-API-KEY" codex --config 'mcp_servers.autotalk_main.url="https://mcp.autotalk.io/v1"' --config 'mcp_servers.autotalk_main.env_http_headers={"x-api-key"="AUTOTALK_API_KEY"}'
Mantén el formato env_http_headers mostrado arriba para que Codex lea el token desde AUTOTALK_API_KEY.
Configuración de Claude Code
Registra el servidor MCP de AutoTalk e inicia Claude Code:
claude mcp add --transport http autotalk_main https://mcp.autotalk.io/v1 --header "x-api-key: sk-YOUR-API-KEY" && claude
Herramientas disponibles
43 herramientas en 8 categorías.
Documents & data (5)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
create_documentCreate document | Creates a new dynadata document in the database. Returns the created document with its _id. Use this to persist agents, workflows, services, and other dynadata types for real testing. Returns the created document, including its `_id`. Any storage refs it contains are resolved into `structuredContent.mediaRefs` — NOT into the document body, and not under an `_mcp` key (that wrapper exists only on the resource-read path). | — |
delete_documentDelete document | Soft-deletes (tombstones) a dynadata document by ID. The document will be excluded from future reads but can be recovered. Returns {deleted: true, tombstoned: true} — NOT the deleted document. | destructivo |
get_documentGet document | RETURNS the full document; storage refs it contains are resolved into `structuredContent.mediaRefs`, not into the document and not under `_mcp`. There is no field-projection parameter, so use query_documents with `fields` when you only need part of a large document. Reads a single dynadata document by type and ID. Respects ACL scoping and excludes tombstoned (soft-deleted) documents. | solo lecturaidempotente |
query_documentsQuery documents | Queries dynadata documents by structured filter tree. Returns matching documents with their _ids. Use this to find documents by field values before reading or updating them. The filterTree is a typed AST: each condition has {fieldPath, kind, operator, value}, and groups combine conditions with and/or. Example: {type: 'group', operator: 'and', children: [{type: 'condition', fieldPath: 'name', kind: 'string', operator: 'contains', value: 'acai'}]}. TO SEARCH BY CONTENT, filter on the text field itself — `contains` is the content-search operator. Do NOT page through a large collection chronologically looking for a match: add a `contains` condition and let the server find it. Example, finding messages in one conversation that mention a word: {type:'group',operator:'and',children:[{type:'condition',fieldPath:'conversationId',kind:'objectId',operator:'eq',value:'<id>'},{type:'condition',fieldPath:'body.text',kind:'string',operator:'contains',value:'refund'}]}. Always pair a content filter with a narrowing condition (conversationId, a date range, a status) — an unbounded contains scans the whole collection. `fieldPath` must be the REAL path in the stored document, which for union-typed fields is nested (message text lives at `body.text`, not `text`). When you are not certain of a path, call get_model_definition for the type FIRST — one call there is cheaper than a failed query plus a retry. Per-kind operator allowlist: string/array supports eq|ne|in|contains|equals|startsWith|endsWith; enum (select fields) supports eq|ne|in|equals; number/date supports eq|ne|gt|gte|lt|lte|in; boolean eq|ne; objectId/ref/refArray eq|ne|in. `kind` is accepted for readability but IGNORED — the real kind comes from the model schema, so a wrong kind never fails and a right one never rescues a bad fieldPath/operator pairing. contains/equals/startsWith/endsWith are case-insensitive LITERAL matches: regex metacharacters in `value` are escaped, not interpreted. Limits, all hard 400s: nesting depth 6, 100 conditions total, 64KB payload, 256 bytes per match value. Use `expand` to hydrate related documents in a single round-trip: pass [{field: 'contactId', as: '_contact', fields: ['name', 'avatar']}] to follow a ref/refArray field; the target collection is resolved server-side from the source model's ref declaration, the trusted ACL stage is injected automatically, and the inner `fields` array is allowlisted against the target model's exposed (non-denied) fields. Single-ref expansions emit one object under `as`; refArray expansions emit an array. RETURNS a bare ARRAY of documents — no wrapper object, no total count, no cursor. The only end-of-results signal is a page shorter than `limit`. If you only need HOW MANY, call count_documents instead — it takes the same filterTree and answers in one call; paging this tool to count rows wastes many round-trips. Storage refs found in the results are resolved into `structuredContent.mediaRefs`. | solo lecturaidempotente |
update_documentUpdate document | RETURNS the full updated document (with _id). Storage refs it contains are resolved into `structuredContent.mediaRefs`, not into the document and not under `_mcp`. Updates an existing dynadata document by ID. Returns the updated document. Use this to iterate on agents, workflows, or any dynadata type between test runs. | destructivo |
Functions & workflows (3)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
execute_functionExecute function | RETURNS whatever the function itself returns, unwrapped — the shape is function-specific, so read its `arguments` and description from list_functions first. Executes a server-side function on a dynadata document or collection. Handles ACL checks, argument validation, hidden conditions, and method dispatch. For document-scope functions, pass the document ID. For collection-scope functions, ID is optional. | destructivo |
list_functionsList functions | RETURNS an array of {name, scope, aclAction, arguments:[{name, type, description, required, default, enum}]}; that `arguments` list is the contract for execute_function's `args`. Lists executable server-side functions for a dynadata type. Returns each function's name, scope (document or collection), required ACL action, and argument schema. UI-scope functions (frontend-only) are excluded. When 'id' is provided, the list is filtered to functions visible for that specific document. | solo lecturaidempotente |
test_workflowTest workflow | RETURNS {logs:[{level, message, meta, ...}], responses:[messages produced], returnValue, returnError?} — `logs` is the per-step trace and `returnValue` is the evaluated returnExpression. Tests a manual workflow by executing its steps against an in-memory backend. Returns execution logs, step outputs, and the return value. Steps are capped at 25. Database records created during execution are cleaned up automatically. | — |
Files & media (4)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
begin_file_uploadBegin file upload | Starts a two-step file upload for this tenant. Returns a signed PUT URL and an uploadIntentId. Exactly: {uploadIntentId, uploadUrl, requiredHeaders, bucket, fullPath, expiresAtMs}. The PUT is a plain HTTP request you must make yourself — this server has no tool that performs it, so an MCP-only client with no HTTP capability should use upload_file_inline instead. Execute an HTTP PUT of the file bytes to the returned uploadUrl within 15 minutes, using the requiredHeaders exactly as given, including Content-Type, then call complete_file_upload with the uploadIntentId to finalize. Path and bucket are chosen server-side — do not pass a fullPath. Subject to the tenant's storage quota. | — |
complete_file_uploadComplete file upload | Finalizes a two-step upload after the HTTP PUT succeeds. Verifies that the object landed in storage with the expected size/mime/metadata and emits the canonical finalize event (the tenant is billed for the bytes at this point). Returns {bucket, fullPath} suitable for inspect_media or to embed in a dynadata document field. | — |
delete_fileDelete file | RETURNS {deleted: true, existed, bucket, fullPath} — `existed:false` means the object was already gone (the call still succeeds). Deletes a storage object owned by this tenant. Verifies the object belongs to the tenant (by path prefix or customMetadata.companyId), then removes it and emits the canonical delete event so the byte count is released from storage quota. Remove references from documents first — otherwise the document field will still point at a missing object. | destructivo |
inspect_mediaInspect media | RETURNS {bucket, fullPath, name, uri, mimeType, size, mediaKind, updated, timeCreated, customMetadata, signedUrl}, plus `source` when read through a document and `analysis` when analysis='transcript'. Image dimensions and audio/video duration are NOT included — only byte `size` and `mimeType`. Images are additionally inlined as model-readable content (`imageInlined: true`, or `imageInlineError` if inlining failed). Reads a storage media reference from a dynadata document or direct {bucket, fullPath}. Returns metadata, a temporary signed URL, and model-readable image/file content when supported. Office documents (docx/xlsx/pptx families) are inlined as extracted plain text (`fileInlined: true`; spreadsheets render one CSV section per sheet). For document refs, pass type, id, and path (e.g. type='messages', id='...', path='body.file'). For audio messages, pass analysis='transcript' to produce a non-persistent on-demand transcription when none is stored. | — |
Messaging (1)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
send_messageSend message to contact | RETURNS a dispatch receipt: {_id, conversationId, contactId, channelId, type, status:'sent', platformMid, timestamp}. WARNING — `status` is a receipt field, not a stored one: the message document has no `status` field, and its delivery state lives in `currentStatus` whose enum is ERROR|PENDING|AUTOTALK_ACK|SERVER_ACK|DELIVERY_ACK|READ|DELETED|PLAYED — 'sent' is not among them. Do not feed this value back into a query; to follow delivery, re-read the message by its returned `_id` and look at `currentStatus`. Sends an outbound message on the tenant's connected channels. Accepts exactly one of {conversationId, contactId}. With contactId, the conversation is found-or-created on the contact's channel. Body shape per type: text={text}; image/audio/video/document/ptv={file:{bucket,fullPath}, text?=caption}; template={template:{templateId, locals?}}; reaction={messageId, emoji}; options={title, description?, options:[{label,value?}]}; poll={title, options:[{label,value?}], selectableCount?}; vote={pollMessageId, selectedOptions:[option label(s)]}; location={location:{type:'Point', coordinates:[longitude, latitude]}, text?, address?}; contacts={text?, contacts:[{displayName, phones:[{phone|waId}]}]}; album={items:[{type:'image'|'video', file, text?}] (2-30)}; event={name, startTime(ISO 8601), description?, endTime?, location?, call?}; product={product:{productId, title, currencyCode, priceAmount1000, file}, businessOwnerJid, body?, footer?}. Set viewOnce=true to send image/video/audio as view-once (WhatsApp Web channels; WhatsApp has no view-once documents). Enforces the tenant's monthly messages_out quota before dispatch. Channel-side rejections (WhatsApp 24h window, template required, media too large, etc.) surface as message_send_failed with a details.summary string from the channel — retry with type='template' for WhatsApp re-engagement. album/event/product/ptv/viewOnce dispatch on WhatsApp Web (lancer/evolution) channels only. | — |
Usage & billing (2)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
get_mcp_server_statsGet MCP server stats | RETURNS a flat object of counters (no arguments). Returns tenant-scoped MCP subscription, stream watcher, backpressure, and notification counters for this server process. | solo lecturaidempotente |
get_usageGet usage and limits | RETURNS {period:{granularity, start, end}, meters:{<meter>: number}, computeBreakdown:{<category>: number}, storage:{bytesUsed, filesCount, purposeBytes, categoryBytes, recomputedAt, limitBytes, softThresholdPct}, database:{same shape}, plan:{_id, name, planKey}|null, limits, subscription:{_id, status, currentPeriodStart, currentPeriodEnd, cancelAtPeriodEnd}|null}. Compare `meters[key]` against `limits` for headroom; `period` is a calendar window (UTC), which is NOT necessarily the subscription period in `subscription`. Returns the tenant's current usage snapshot: billed meter totals (compute_ms, tokens_in/out, stt_ms, transcoding_ms) plus internal counters (messages_in/out), compute breakdown by category, storage + database bytes used, active plan with resolved limits (monthly meter caps, storage/database byte caps, static limits, employee seats), and subscription period info. Defaults to the current calendar month (UTC). Pass granularity: 'day' for today-only totals. | solo lecturaidempotente |
Custom types (5)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
create_custom_typeCreate custom type | Creates a new tenant-scoped Custom Type. RETURNS the stored definition plus a 'type' key — the ct:<slug> string you pass to every document tool afterwards. Subsequent calls to create_document('ct:<slug>', ...) will use its field definitions. REMINDER — cel.label / cel.tooltip (on fields, type, tabs, sections) are CEL expressions: quote literals as "'Nome'" or use "t('name')", NOT "Nome". Authoring type-level cel.label REQUIRES cel.preview.lines (pass "preview":null to opt out). Call get_guide('custom-types') for the full DSL. Max 25 types per tenant, 60 fields per type. | — |
delete_custom_typeDelete custom type | RETURNS {deleted: true, slug}. Soft-deletes a custom type. Subsequent CRUD calls to ct:<slug> will fail until the type is re-created. Existing docs are also soft-deleted by the afterDelete hook. | destructivo |
get_custom_type_definitionGet custom type definition | RETURNS {definition} — the fields/tabs/sections/cel definition for a tenant's custom type. Accepts either the raw slug ('leads') or the prefixed form ('ct:leads'). This is the call whose output you must re-send to update_custom_type, because that tool REPLACES the field list rather than merging into it. | solo lecturaidempotente |
list_custom_typesList custom types | Returns the tenant's user-defined Custom Types (ct:<slug>). Use this to discover what tenant-authored models exist before calling create_document/query_documents on a ct:<slug> type. | solo lecturaidempotente |
update_custom_typeUpdate custom type | RETURNS the updated definition plus a 'type' key (the ct:<slug> string). Updates fields/name/description/indexedPaths/cel/tabs/sections on an existing custom type. Slug is immutable. DESTRUCTIVE — `patch.fields` REPLACES the entire field list rather than merging into it. To add or change one field you must call get_custom_type_definition first and re-send every existing field alongside your change; any field you omit is deleted, and its data becomes unreachable. The same wholesale-replace applies to indexedPaths, tabs and sections. Same CEL rules as create_custom_type: cel.label / cel.tooltip must be quoted CEL literals or t('key') lookups (not bare display strings); authoring type-level cel.label requires cel.preview.lines (or explicit preview:null). | destructivo |
Support (6)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
create_support_ticketOpen an AutoTalk support ticket | RETURNS the created ticket document, including its `_id` — pass that id to reply_to_support_ticket / resolve_support_ticket / reopen_support_ticket. Files a support ticket with AutoTalk's team on behalf of this tenant. Body is markdown. Attach screenshots or logs via attachments:[{bucket, fullPath}] obtained from begin_file_upload/upload_file_inline. Priority sets the response target (urgent 2h, high 8h, normal 24h, low 72h). | — |
get_support_ticketRead one support ticket | RETURNS the full ticket document including its `replies` array. Poll this to see whether support has answered — tickets have no resource URI, so there is no subscription for them. | solo lecturaidempotente |
list_support_ticketsList this tenant's support tickets | RETURNS an array of ticket documents, newest activity first. Lists the tenant's own AutoTalk support tickets. | solo lecturaidempotente |
reopen_support_ticketReopen a resolved support ticket | RETURNS the updated ticket document. Reopens a resolved ticket when the problem came back. Only 'resolved' tickets can be reopened. | idempotente |
reply_to_support_ticketReply on a support ticket | RETURNS the updated ticket document. Adds a customer reply to one of this tenant's support tickets and notifies the support team. Body is markdown. Replying to a resolved ticket reopens it; closed tickets are terminal — open a new one. | — |
resolve_support_ticketMark a support ticket resolved | RETURNS the updated ticket document. Marks one of this tenant's tickets resolved because the problem is fixed. Reversible: replying to a resolved ticket reopens it. | idempotente |
Schema & references (17)
| Herramienta | Descripción | Etiquetas |
|---|---|---|
count_documentsCount documents | RETURNS {count} — the number of documents matching a filter, in ONE call. USE THIS whenever the answer is a NUMBER: 'how many messages do I have', 'do I have any open conversations', 'quantos canais tenho'. Do NOT call query_documents and count the rows — that pages through the documents themselves and costs many round-trips for a single number. Takes the SAME `type` and `filterTree` grammar as query_documents (see that tool for the filter-tree AST, the per-kind operator allowlist, and the fieldPath rules); it has no sort/skip/limit/fields/expand because none of them change a count. Omit filterTree to count every document of the type that you can see. The count reflects exactly the documents query_documents would return for the same filter — same permissions, same match. | solo lecturaidempotente |
fetch_user_docFetch user doc | RETURNS the page markdown. Get valid slugs from list_user_docs — do not guess them. Fetches the markdown content for a single AutoTalk user-facing documentation page. Accepts a slug (e.g. 'workflows/creating-a-workflow') or full URL. Use list_user_docs to discover available paths. | solo lecturaidempotente |
generate_documentGenerate a document | RETURNS {bucket, fullPath, size, contentType, fileName, format} — a storage ref usable with inspect_media, send_message attachments, and document file fields. Renders a document server-side (#185 Layer 2): format 'pdf'/'docx'/'md' from content.markdown (headings, paragraphs, lists, code blocks, simple tables), format 'xlsx'/'csv' from content.sheets ([{name?, rows: string[][]}]; csv uses the first sheet only). Deterministic rendering, not code execution — for computed spreadsheets/charts use the Python workflow action instead. Output capped at 10 MiB; counts toward the tenant's storage quota. | — |
get_action_definitionGet action definition | Returns full action definition with all fields, constraints, and outputs. Use the actionType from list_action_types. | solo lecturaidempotente |
get_cel_referenceGet CEL reference | Returns available CEL variables and functions for a given context (workflow, assistant, or form). | solo lecturaidempotente |
get_guideGet guide | Returns cookbook-style guides for building workflows, agents, querying data, and sending messages. Call without topic to list available guides. For user-facing product documentation (UI walkthroughs, channels, billing), use list_user_docs / fetch_user_doc instead. | solo lecturaidempotente |
get_model_definitionGet model definition | RETURNS {name, collectionName, description, fields:[{name, type, description, required, enum, refModel, fields|variants}], functions:{...}, mcp:{tips:[...]}}. The `mcp.tips` array is the model's own usage guidance — which fields express what, which paths are NOT filterable, and worked filter examples; read it before building a filterTree. Returns field definitions for a dynadata model type or embedded definition with descriptions, types, constraints, and nested structure. Supports nested assistant/common defs like assistants.platform.ActorJson. IMPORTANT: workflows.steps (array), messages.body (discriminated_union), and agents.platform (nested_form) ARE structurally validated by Zod, so validate_document DOES catch wrong shapes there. Only genuinely Mixed fields (schema.type='mixed', compiled to z.any()) accept arbitrary JSON: custom_types.cel, custom_types.calendar, action_tokens.payload, action_tokens.meta, and conversations.assistantData.context — plus deep leaf nodes such as step action params and message body variant content/value. Mixed shapes are enforced at render/execute time, not by Zod; check the model's `mcp.tips` and the relevant authoring guide (custom-types.md, docs-reference-cel-editor.md) before authoring such fields. | solo lecturaidempotente |
get_model_json_schemaGet model JSON schema | Returns the JSON Schema (from Zod) for a dynadata model type or embedded definition. Supports nested assistant/common defs like assistants.platform.ActorJson. | solo lecturaidempotente |
get_model_relationshipsGet model relationships | Returns the relationship graph between dynadata models, showing which fields reference which models. Auto-extracted from ref fields in all model definitions. | solo lecturaidempotente |
get_transcription_jobGet transcription job | RETURNS {jobId, status, progress, enginePath, providerType, diarized, speakerCount, outputs, error}. `outputs` holds storage full paths to the rendered transcript files, not the transcript text itself — read one with inspect_media. Returns a transcription job's status, progress and — once done — the storage full paths of the rendered transcript outputs. | solo lecturaidempotente |
list_action_typesList action types | RETURNS an array of actionType strings — the wire keys, which the docs.autotalk.io action reference pages do NOT print (they use UI labels). Lists all workflow/agent action types. Use get_action_definition for full field details on a specific action. | solo lecturaidempotente |
list_dynadata_typesList dynadata types | RETURNS an array of type names — the legal values for every `type` argument on this server. Lists available dynadata model types. Optionally include embedded assistant/common definitions such as assistants.platform.ActorJson. | solo lecturaidempotente |
list_user_docsList user docs | Lists all AutoTalk user-facing documentation pages (UI walkthroughs, channel setup, billing, etc.) live from docs.autotalk.io. Returns slugs + titles + descriptions for each page. Use fetch_user_doc to load the markdown for a specific slug. | solo lecturaidempotente |
search_example_documentsSearch example documents | RETURNS matching example summaries, or full example documents when includeDocuments is set. These are AutoTalk's shipped EXAMPLES, not this tenant's data — use query_documents for that. Searches curated example dynadata documents to find reusable patterns and optionally return one or many full documents. Do NOT search project files for examples. | solo lecturaidempotente |
transcribe_storage_fileTranscribe storage file | RETURNS {jobId, status, enginePath, providerType, diarized} IMMEDIATELY — the transcript is NOT in this response. Poll get_transcription_job with the returned jobId, then read the file it names under `outputs`. Enqueues an ASYNC transcription job for an audio file in company storage. Uses the given transcription profile (or the company default); diarize profiles return speaker-labeled outputs. Outputs (txt/srt/vtt/json) are written next to the source file as <name>.transcript.<jobId>.<ext> (job-keyed, so re-running the same audio never overwrites an earlier transcript); read the exact paths off the job's `outputs`. Poll get_transcription_job for progress. For transcribing a chat AUDIO MESSAGE use inspect_media with analysis='transcript' instead. | — |
upload_file_inlineUpload a small file (one step) | Uploads a small file in a single call by passing its base64-encoded bytes directly — no separate HTTP PUT. Returns {bucket, fullPath} suitable for inspect_media or to embed in a document field / send_message. Path and bucket are chosen server-side. Bounded by the MCP request body limit (~1 MB of decoded bytes); for larger files use begin_file_upload + complete_file_upload. Subject to the tenant's storage quota. | — |
validate_documentValidate document | RETURNS {valid: true} or {valid: false, errors: [...]}. Validates a document against the model's Zod schema. Supports both full documents and partial validation. For large documents (agents, workflows), use the 'fields' parameter to validate only specific top-level fields, or validate fragments directly using embedded types (e.g. type='assistants.platform.OpenAiJson'). Returns {valid:true} or {valid:false, errors:[...]}. CAVEAT: A {valid:true} result does NOT guarantee the payload is renderable/executable — fields declared as schema.type='mixed' (Mongoose Mixed) are accepted as opaque JSON. Note that workflows.steps (array), messages.body (discriminated_union), and agents.platform (nested_form) are NOT Mixed — they are structurally validated, so wrong shapes there ARE caught. Genuinely Mixed fields include custom_types.cel, custom_types.calendar, action_tokens.payload, action_tokens.meta, conversations.assistantData.context, and message body variant content/value leaves. For those, cross-reference the model's `mcp.tips` plus the authoring guide, or validate the embedded fragment type explicitly (e.g. type='assistants.platform.OpenAiJson'). | solo lecturaidempotente |
Consejos de uso
- El servidor está autodocumentado: llama a
get_guidesin argumentos para listar las guías de recetas integradas (building-agents,building-workflows,custom-types,querying-data, …) y espejos offline de esta documentación, y lee la guía relevante antes de crear algo — por ejemplo,get_guide("custom-types")antes de crear un Custom Type. - Comienza con
list_dynadata_typespara descubrir modelos disponibles, luego usaget_model_definitionpara detalles de los campos. - Usa
search_example_documentspara encontrar patrones reutilizables antes de construir agentes o workflows desde cero. - Usa
validate_documentpara verificar tu documento antes de crearlo concreate_document. - Usa
test_workflowpara iteración rápida — ejecuta workflows en memoria y limpia automáticamente. - Para documentos grandes, valida secciones específicas usando el parámetro
fieldso tipos embebidos.
Próximos pasos
- Tokens de API — Crea y gestiona tokens de API
- Webhooks — Configura notificaciones de eventos salientes