Когда впервые пишешь собственную интеграцию с amoCRM через REST API, сами запросы к сделкам, контактам или компаниям обычно оказываются не самой сложной частью.

Основной вопрос возникает раньше:

как один раз авторизовать интеграцию, а затем поддерживать доступ к amoCRM без постоянного ручного получения ключей?

1–2 года назад я реализовывал такую интеграцию на PHP. Токены хранили в MySQL, а работу с OAuth разбили на несколько отдельных файлов.

Сейчас решил восстановить общую архитектуру этой реализации.

Если убрать детали, OAuth‑интеграция выглядит довольно просто:

Authorization Code → Access Token + Refresh Token → сохранение в БД → запросы к REST API → обновление токенов → повторное сохранение в БД.

Код авторизации берется в AmoCRM:

В общем все эти значения помещаются в data и потом отсылаются через curl:

subdomain = 'https://xxx.amocrm.ru/'; //Поддомен нужного аккаунта
$link = 'https:/xxx.amocrm.ru/oauth2/access_token'; //Формируем URL для запроса

$data = [
    'client_id' => '6a2d4741-xxxx-xxxx-xxxx-92277723dcea', // ID интеграции
    'client_secret' => 'PZp1wm7VpAUBvpTzADj4CRHjmuvRR4PwVS7EqVxxxNvpMglIWK8uy6KWSgxxvdjY', //секретный ключ
    'grant_type' => 'authorization_code',
    'code' => 'def50200exxxx8a701f977d7e9151d7b02da0000001d28e4a100000004c2e7350000xxb0f8fcd6b982615f86242baf3eea6ee5cbe282f19e8fd3d973ab858afa6691701f83c61f3e02af1029334772747c6242dc50e6e0bfe31e45666a8be5d93f0d5242300e2006c4bfe8cd4fa7c661f1ed5a4f290b8b283b44c3c22e247aa6abd587d7ce666ee6956ec8cb281ef6813ba6494f4eba97a8b20486cd3dff0ae6d486a195ae50755e13cf00f9608d3e699c5d7baefdbfc22da8ee21d1af69e6183abba81ba2a174341ce1e62c2dd7bbf956aee3624265d5bd9c098ffc24d9071d63b7c6e6db1ba5d55a853483ac77e42d6ed8ca32be8ccb2e7ee488716da9f8c21be27489f7d6ceadebe8d120b22233e053c1659fd872b4eefc756cd4770d23e9a48707d492051e417995193f51e6322c339d669e3298aee671ef2caa8c61b662d34685b7f8186be04a5f495a2123bacebfa7f2f8a9901260f8ba4757ab252849af7920445a8fe8397c78bbb94900e0f7f8984f18efb235bb378d5413c26100338c91d1d21dd15e4641ee85dc2b3187f6bb20a34fb94993302b18afe4ea577fdc9f8bcaf4a550eb7fdc41c206b318f7f210f696787fe0cbfe50843ce86849cf5dc6b834cb752cbe6e86df89b3402b7d3f4c28c3588f5908afac5452ee76a1b1b768aa25a5769cf272fa993101f07de20c28', 
    'redirect_uri' => 'https://xxx.ru/',
];


$curl = curl_init(); 
curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0');
curl_setopt($curl,CURLOPT_URL, $link);
curl_setopt($curl,CURLOPT_HTTPHEADER,['Content-Type:application/json']);
curl_setopt($curl,CURLOPT_HEADER, false);
curl_setopt($curl,CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2);
$out = curl_exec($curl); 
var_dump($out);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);

$code = (int)$code;
$errors = [
    400 => 'Bad request',
    401 => 'Unauthorized',
    403 => 'Forbidden',
    404 => 'Not found',
    500 => 'Internal server error',
    502 => 'Bad gateway',
    503 => 'Service unavailable',
];

try
{
    if ($code < 200 || $code > 204) {
        throw new Exception(isset($errors[$code]) ? $errors[$code] : 'Undefined error', $code);
    }
}
catch(\Exception $e)
{
    die('Ошибка: ' . $e->getMessage() . PHP_EOL . 'Код ошибки: ' . $e->getCode());
}

Схема такая:

Authorization Code

POST /oauth2/access_token

Access Token
Refresh Token

По итогу получает в обратку Access Token и Refresh Token и их записываем в БД себе, мы в MySQL записывали.

function save_token ($access_token, $refresh_token) {

                $config = [

                'host' => '127.0.0.1',
                'db_name' => 'xxx',
                'username' => 'xxxx',
                'password' => "xxx",
                'port' => 3308,
                ];

                $d = "mysql:host={$config ['host']};port={$config["port"]};dbname={$config ["db_name"]};charset=utf8";
                try {
                $pdo = new PDO($d, $config ["username"], $config ["password"]);
                echo('<br> Подключение успешно к БД <br>');
                } catch (PDOException $e) {
                    echo "Connect failed: ". $e -> getMessage();
                }

        $insert = "INSERT INTO `AmoAuth`(`access_token`, `refresh_token`) VALUES ('$access_token','$refresh_token')";
        try {
                        $pdo -> query($insert);
                        echo("Токены успешно сохранены");
            } catch (\Throwable $th) {
                        echo("Ошибка записи в БД $th");
                    }

}

Все, $access_token есть. Зачем? Что бы потом его использовать в запросах к AmoCRM, например:

GET /api/v4/leads
GET /api/v4/contacts
POST /api/v4/leads

Потому что если access_token не валиден, то запросы не пройдут, он нужен далее для работы.

Access token amoCRM имеет ограниченный срок действия, поэтому его надо постоянно обновлять и для этого нужен Refresh Token.

Логика получается следующая:

Есть Access Token — Токен ещё действителен?
да → REST API
нет → Refresh Token → новый Access Token + новый Refresh Token → База

Если Access Token истек, то надо переобновить.

А что бы понять — надо проверить на валидность:

public function access_token_valid ($access_token) { //проверка access_token на валидность
    $this -> errorslogservice -> errors_log ("access_token_valid запускается");
    $url = 'https://xxx.amocrm.ru/api/v4/account';

    $headers = [
        'Authorization: Bearer ' . $access_token
        ];
    $curl = curl_init(); 
    curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl,CURLOPT_ENCODING, '');  
    curl_setopt($curl,CURLOPT_MAXREDIRS, 10);  
    curl_setopt($curl,CURLOPT_TIMEOUT, 0);  
    curl_setopt($curl,CURLOPT_FOLLOWLOCATION, true);  
    curl_setopt($curl,CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);  
    curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0');
    curl_setopt($curl,CURLOPT_URL, $url);
    curl_setopt($curl,CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl,CURLOPT_HEADER, false);
    curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1);
    curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2);

    $out = curl_exec($curl); 
    $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);

    $this -> errorslogservice -> errors_log ($out);
    $res = json_decode($out);
    return isset($res -> status);
}

Как взять токен с БД писать не буду и так понятно, обычный SQL запрос внутри PHP с pdo по типу такого $query = $this → pdo → query($request).

Если оказался не валидным — надо запросить новый Access Token, то формируем код где дата Refresh Token:

private function refresh_amo_token ($refresh_token) { //получение нового access_token и refresh_token через refresh_token
    $this -> errorslogservice -> errors_log ("refresh_amo_token запускается");
    $url = 'https://xxx.amocrm.ru/oauth2/access_token';
    $data = [
    'client_id' => 'xxxxxxxx-84f7-4bc9-911c-92277723dcea',
    'client_secret' => 'PZp1xxxxxxxxxxxxxxRR4PwVS7EqV7G1NvpMglIWK8uy6KWSg0AvdjY',
    'grant_type' => 'refresh_token',
    'refresh_token' => $refresh_token, 
    'redirect_uri' => 'https://xxx.ru/',
    ];
   
    $headers = ['Content-Type:application/json'];
    $curl = curl_init();
    curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl,CURLOPT_ENCODING, '');  
    curl_setopt($curl,CURLOPT_MAXREDIRS, 10);  
    curl_setopt($curl,CURLOPT_TIMEOUT, 0);  
    curl_setopt($curl,CURLOPT_FOLLOWLOCATION, true);  
    curl_setopt($curl,CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);  
    curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0');
    curl_setopt($curl,CURLOPT_URL, $url);
    curl_setopt($curl,CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl,CURLOPT_HEADER, false);
    curl_setopt($curl,CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1);
    curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2);

    $out = curl_exec($curl);
    $this -> errorslogservice -> errors_log ($out);
    $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);
    if ($code >= 400) {
            throw new Exception ("ошибка API AmoCRM $out");
    }
    $tokens = json_decode($out);
    if (!isset ($tokens -> access_token)) {
        throw new Exception ("Ошибка получения токена".json_encode($tokens));
    }
    return $tokens;
}

В ответ получаем новую пару Access Token и Refresh Token и перезаписываем их в БД себе.

И так по кругу, Access Token — не валидный — запросили новый — перезаписали в БД — используем в запросах Rest API к Амо.

Как вариант слышал что можно ещё сохранять expires_at что бы каждый раз не проверять валидность. Мы же делали каждый раз в curl проверяя, наверное это не очень оптимально если запросов будет много, но при малом‑среднем количестве работает нормально:

   $access_token = $this -> tokensService -> get_token();
   if ($this -> tokensService -> access_token_valid ($access_token)) { 
   $access_token = $this -> tokensService -> refresh_token ();
   };

И погнало по кругу.

Если свести всё к нескольким правилам:

  1. Authorization Code нужен в основном для первоначальной авторизации.

  2. Он обменивается на access_token и refresh_token.

  3. Access_token используется для REST‑запросов.

  4. Когда access token заканчивается, используется refresh_token.

  5. После refresh amoCRM выдаёт новую пару Access + Refresh.

  6. Оба новых токена необходимо сохранить.

  7. Старый Refresh token после обмена использовать нельзя.

При колхозном подходе все это можно запихать в 1 файл PHP, но по правильному разбивать на: Authamo.php, AmoAPIService.php, TokenService.php — что бы каждый выполнял свою функцию.

Authamo.php — авторизация первый раз

AmoAPIService.php — это как раз регулярные запросы к API, когда токен уже есть

TokensService.php — это как раз работа с токенами если при проверке IF в AmoAPIService.php вылетело что невалидный.

Комментарии (4)


  1. prishelec
    26.08.2026 14:24

    Сейчас можно создавать долгодействующий токен. Что очень удобно..


    1. IvanDB Автор
      26.08.2026 14:24

      Точно, есть такое. Это скорее всего подойдет для индивидуальной интеграции, но для приложений маркета, как слышал (пока ещё не писал), там только с заменой скорее всего т.к. клиенты разные. Спасибо за информацию.


      1. prishelec
        26.08.2026 14:24

        Сначала был вечный токен. Потом временный через OAuth 2.0.

        У меня был алгоритм. Если получаем ошибку 401 то обновляем токен и заново используем метод который использовали. Но бывает проблема на сервере и все токен не обновиться. В теории да, можно предусмотреть дополнительную обработку и т.д.

        По поводу ваших клиентов. Ну напишите в мануале где брать токен без всяких OAuth 2.0. Меньше «головной боли» будет.

         


        1. IvanDB Автор
          26.08.2026 14:24

          Если клиент будет себе ставить приложение с маркета Амо, то врят ли он будет заморачиватсья с токенами, им надо нажать кнопки и что бы все работало. Хотя может и будут копировать токен, надо будет смотреть, потому что если у готового приложения OAuth 2.0 будет отлетать из за того что токен не обновить, то проще и правда сделать инструкцию откуда скопировать долгосрочный и куда вставить. Спасибо за идеи.