{"id":20517212,"url":"https://github.com/shevernitskiy/amo","last_synced_at":"2025-12-28T09:08:38.967Z","repository":{"id":154029869,"uuid":"630563357","full_name":"shevernitskiy/amo","owner":"shevernitskiy","description":"♿amoCRM API client","archived":false,"fork":false,"pushed_at":"2024-12-27T16:27:12.000Z","size":280,"stargazers_count":8,"open_issues_count":1,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T14:55:28.837Z","etag":null,"topics":["amo","amocrm","api","client","deno","javascript","node","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shevernitskiy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-20T16:42:03.000Z","updated_at":"2024-12-27T16:27:00.000Z","dependencies_parsed_at":"2023-11-19T10:22:37.343Z","dependency_job_id":"8cce1525-e13d-41b6-bcc1-56eadd95506e","html_url":"https://github.com/shevernitskiy/amo","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shevernitskiy%2Famo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shevernitskiy%2Famo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shevernitskiy%2Famo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shevernitskiy%2Famo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shevernitskiy","download_url":"https://codeload.github.com/shevernitskiy/amo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248804782,"owners_count":21164131,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amo","amocrm","api","client","deno","javascript","node","typescript"],"created_at":"2024-11-15T21:34:20.444Z","updated_at":"2025-12-28T09:08:38.851Z","avatar_url":"https://github.com/shevernitskiy.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ♿amoCRM API client\n\n[![npm](https://img.shields.io/npm/v/@shevernitskiy/amo?logo=npm\u0026style=flat\u0026labelColor=000)](https://www.npmjs.com/package/@shevernitskiy/amo)\n[![deno module](https://shield.deno.dev/x/amo)](https://deno.land/x/amo/mod.ts)\n[![JSR](https://jsr.io/badges/@shevernitskiy/amo)](https://jsr.io/@shevernitskiy/amo)\n![dependencies](https://img.shields.io/badge/dependencies-0-green?style=flat\u0026labelColor=000)\n[![license](https://img.shields.io/github/license/shevernitskiy/amo?style=flat\u0026labelColor=000)](https://github.com/shevernitskiy/amo/blob/main/LICENSE)\n\nThis is a simple wrapper client for the amoCRM REST API. It covers almost all API modules and endpoints. Also, it\nmanages to token refreshing and webhook handling.\n\n\u003e ⚠️Due to awful API [documentation](https://www.amocrm.ru/developers/content/crm_platform/api-reference) with tons of\n\u003e mistakes, inaccuracies, examples mismatch and wrong types, lib may provide wrong typing (pls consider to make a PR or\n\u003e issue at least).\n\n## Progress\n\n- ### Lib\n  - [x] NPM \u0026 Node support\n  - [x] examples (not so much)\n  - [x] maybe some test (webhook atm)\n  - [x] readme (draft)\n- ### API\n  - [x] Account\n  - [x] Leads\n  - [x] Unsorted\n  - [x] Pipelines and Stages\n  - [x] Contacts\n  - [x] Companies\n  - [x] Catalogs\n  - [x] Products\n  - [x] Links\n  - [x] Tasks\n  - [x] Custom Fields\n  - [x] Tags\n  - [x] Events\n  - [x] Notes\n  - [x] Customers\n  - [x] Statuses\n  - [x] Segments\n  - [x] Users\n  - [x] Webhooks\n  - [x] Widgets\n  - [x] Calls\n  - [x] Talks\n  - [x] Sources\n  - [x] Salesbot (api method)\n  - [x] Short Links\n  - [x] Chat Templates\n  - [x] Files\n  - [ ] Chats\n- ### Helpers\n  - [x] Filter builder\n  - [x] Webhook handling\n  - [x] Error handling\n  - [ ] Salesbot interactions\n\n# Usage\n\n## Installation\n\n\u003cimg height=\"18\" src=\"https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/icons/nodejs.svg\"\u003e Node.JS\n(versions \u003e=18 are supported because of Fetch API)\n\n```powershell\nnpm i @shevernitskiy/amo\n```\n\n\u003cimg height=\"18\" src=\"https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/icons/deno.svg\"\u003e Deno\n\n```ts\nimport { Amo } from \"https://deno.land/x/amo/mod.ts\";\n```\n\n---\n\n## Basic example\n\nHere is the basic usage scenario. We use previously saved token object here (cause it valid for a long time, so we do\nnot need to refresh it often). More [examples](https://github.com/shevernitskiy/amo/tree/main/examples).\n\n```ts\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { Amo, ApiError, AuthError } from \"@shevernitskiy/amo\";\n\ntry {\n  const auth = {\n    client_id: \"1111-2222-3333\",\n    client_secret: \"myclientsecret\",\n    redirect_uri: \"https://myredirect.org\",\n  };\n\n  const token = JSON.parse(readFileSync(\"./token.json\", \"utf-8\"));\n\n  const amo = new Amo(\"mydomain.amocrm.ru\", { ...auth, ...token }, {\n    on_token: (new_token) =\u003e {\n      console.log(\"New token obtained\", new_token);\n      writeFileSync(\"./token.json\", JSON.stringify(new_token, null, 2), \"utf8\");\n    },\n  });\n\n  const data = await amo.account.getAccount({\n    with: [\"amojo_id\"],\n  });\n\n  console.log(data);\n} catch (err) {\n  if (err instanceof ApiError || err instanceof AuthError) {\n    console.error(err.response);\n  } else {\n    console.error(err);\n  }\n}\n```\n\n---\n\n## Creating client instance\n\nTo create a client instance, you should provide 2 or 3 args to the constructor:\n\n- API domain\n- auth data (may be different)\n- options (optionally)\n\n```ts\nconst amo = new Amo(\"mydomain.amocrm.ru\", auth_object, options_object);\n```\n\n### Options\n\n#### Request queue\n\nAmo backend limits you to _7 reqs/sec_, so the client can manage with it by performing requests concurrently or\nsequently with delay. By default, lib performs requests concurrently (_7reqs/1000ms_ by default). To setup you own\nconcurrency params use:\n\n- `concurrent_request: number` - size of concurrent pool\n- `concurrent_timeframe: number` - timeframe for concurrent pool (ms)\n\nIf you want to use sequential requests, set `request_delay` option param:\n\n- `request_delay: number` (ms) - you can set it to zero, if you want to perform requests as it is\n\n#### Callbacks\n\n- `on_token?: (new_token: OAuth) =\u003e void | Promise\u003cvoid\u003e` - callback, that will be called on _new token_ event (during\n  receiving from a code or refreshing). Lib manages the auth/token stuff for you, but it is strongly recommended to\n  store the new token persistently somewhere you want (fs, db) to provide it on the next app start.\n- `on_error?: (error: Error) =\u003e void | Promise\u003cvoid\u003e;` - default error handler. If provided, it will be called instead\n  of throwing errors. Request lifycycle will not be interrupted and you receive `null` as a response.\n\n---\n\n## Authorization\n\nThe client can authorize you by both methods: auth code and token data. Also, it refreshes the token automatically on\nexpiration.\n\n### Auth by code\n\nUsually, this method is used just once while a fresh app is registered. You provide the code and get the token data.\n\n```ts\nconst amo = new Amo(\"mydomain.amocrm.ru\", {\n    client_id: \"1111-2222-3333\",\n    client_secret: \"myclientsecret\",\n    grant_type: \"authorization_code\",\n    code: \"supersecretcode\",\n    redirect_uri: \"https://myredirect.org\"\n  }, {\n    on_token: (new_token) =\u003e console.log(\"New token obtained\", new_token);\n  },\n})\n```\n\n### Auth by existing token\n\nThis method is used every time after the first authorization by code. The API does not provide the property\n`expires_at`, but lib returns it in `on_token` callback value. If you are using longlive dev token, just keep\n`expires_at` blank.\n\n```ts\nconst amo = new Amo(\"mydomain.amocrm.ru\", {\n    client_id: \"1111-2222-3333\",\n    client_secret: \"myclientsecret\",\n    redirect_uri: \"https://myredirect.org\",\n    token_type: \"Bearer\",\n    expires_in: 86400,\n    access_token: \"megatoken\",\n    refresh_token: \"antohermegatoken\",\n    expires_at: 1682573043856\n  }, {\n    on_token: (new_token) =\u003e console.log(\"New token obtained\", new_token);\n  },\n})\n```\n\n---\n\n## Making requests\n\nThe client provides methods by API category. Each category reflects the docs structure (not endpoints, actually - this\nis one of the strange api architecture things). Here is the schema for calling a method in some category:\n\n```ts\nclient_instance.category.method(...)\n```\n\nSo the real world example will:\n\n```ts\nconst lead = await amo.lead.getLeadById(6969);\n```\n\n### Parameters\n\nSome methods can receive typical request parameters: _order, with, page, limit_ and _filter_\n\n#### With\n\nCan take array of strings.\n\n```ts\nwith: [\"drive_url\", \"amojo_id\", \"amojo_rights\", \"datetime_settings\"]\n```\n\n#### Order\n\nCan take the object.\n\n```ts\norder: { param: \"id\", type: \"asc\" }\n```\n\n#### Filter\n\nFilter is a complex parameter that depends on the method. Lib provides a filter builder to construct filter queries\ndepending on the method. Each filter can take different types of input conditions: single (property = value), multi\n(property = array of values), range (property = from-to), custom fields* , statuses* (*only for leads as I know?). To\nuse the filter builder depending on the constraits of the API method, you should pass the callback that receives filter\ninstance and return it with your params. The instance will be typed, and you will not be able to set the value if it\ndoes not satisfy the method constraints (type of the value will be `never`).\n\n```ts\nfilter:\n((filter) =\u003e\n  filter\n    .single(\"id\", 6969)\n    .multi(\"created_by\", [\"john\", \"smith\"])\n    .range(\"closed_at\", 2418124812, 123124712712));\n```\n\n---\n\n## Webhooks\n\nThe client could handle incoming webhook and acting as event emitter, wich emits typed context to the listener callback\ndepending on the event. To use this possibility, the client provides a typical handler that you could setup to handle\nincoming http requests.\n\nHandler signature is (maybe i'll add _(req, res)_ type for express enojyers later):\n\n```ts\n((request: Request) =\u003e Promise\u003cResponse\u003e);\n```\n\nWebhook handling example. Remember that `webhookHandler()` is a function factory, create handler just once and then use\nit.\n\n```ts\nconst amo = new Amo(\"mydomain.amocrm.ru\", auth_object, options_object);\n\namo.on(\"leads:status\", (lead) =\u003e console.log(lead.id));\n\nconst handler = amo.webhookHandler();\nDeno.serve({ port: 4545 }, handler);\n```\n\n---\n\n## Error handling\n\nThe client throws several types of errors:\n\n- `ApiError`\n- `AuthError`\n- `HttpError`\n- `NoContentError`\n- `WebhookError`\n\n`ApiError` and `AuthError` has additional property response with API error message.\n\nHandling is simple:\n\n```ts\ntry {\n  const amo = new Amo(\"mydomain.amocrm.ru\", auth_object, options_object);\n  const lead = amo.lead.getLeadById(6969);\n} catch (err) {\n  if (err instanceof AuthError) {\n    console.error(\"AuthError\", err.response);\n  } else if (err instanceof ApiError) {\n    console.error(\"ApiError\", err.response);\n  } else if (err instanceof NoContentError) {\n    console.error(\"NoContentError\", err.message);\n  } else if (err instanceof HttpError) {\n    console.error(\"HttpError\", err.message);\n  } else {\n    console.error(\"UnknownError\", err);\n  }\n}\n```\n\nAlso you could use default non-intercepted error handler passed with the `options` to client constructor:\n\n```ts\nconst amo = new Amo(\"mydomain.amocrm.ru\", auth_object, {\n  on_error: (err) =\u003e console.error(\"Amo emits error\", err);\n});\nconst lead = amo.lead.getLeadById(6969);\nif (lead) {\n  // do logic\n}\n```\n\n# Contribution\n\nPull request, issues and feedback are very welcome. Code style is formatted with deno fmt.\n\n# License\n\nCopyright 2023, shevernitskiy. MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshevernitskiy%2Famo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshevernitskiy%2Famo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshevernitskiy%2Famo/lists"}