Developing a request form with CRM integration
A form on the website whose data automatically reaches the CRM is a standard task for lead-generating sites. The manager receives a notification, the lead appears in the funnel, correspondence history is saved. Without manual data transfer.
Popular CRMs and integration methods
| CRM | Integration method | Complexity |
|---|---|---|
| amoCRM | REST API + webhooks | Medium |
| Bitrix24 | REST API / Open Channel | High |
| HubSpot | Forms API / Contacts API | Low |
| Salesforce | REST API / Lead object | High |
| Pipedrive | REST API | Low |
amoCRM integration
class AmoCrmService
{
private string $subdomain;
private string $accessToken;
public function createLead(array $formData): int
{
$resp = Http::withToken($this->accessToken)
->post("https://{$this->subdomain}.amocrm.ru/api/v4/leads", [
[
'name' => "Request from website: {$formData['name']}",
'status_id' => config('amocrm.initial_status_id'),
'pipeline_id' => config('amocrm.pipeline_id'),
'_embedded' => [
'contacts' => [[
'name' => $formData['name'],
'custom_fields_values' => [
['field_code' => 'EMAIL', 'values' => [['value' => $formData['email']]]],
['field_code' => 'PHONE', 'values' => [['value' => $formData['phone'], 'enum_code' => 'WORK']]],
],
]],
],
'custom_fields_values' => [
['field_id' => config('amocrm.source_field_id'), 'values' => [['value' => 'Website']]],
['field_id' => config('amocrm.comment_field_id'), 'values' => [['value' => $formData['message']]]],
],
]
]);
return $resp->json('_embedded.leads.0.id');
}
}
amoCRM OAuth2 tokens
amoCRM uses OAuth2 with refresh_token. Tokens need updating every 24 hours:
class AmoCrmTokenManager
{
public function getValidToken(): string
{
$stored = Cache::get('amocrm_access_token');
if ($stored) return $stored;
// Update via refresh_token
$resp = Http::post("https://{$this->subdomain}.amocrm.ru/oauth2/access_token", [
'client_id' => config('amocrm.client_id'),
'client_secret' => config('amocrm.client_secret'),
'grant_type' => 'refresh_token',
'refresh_token' => decrypt(Setting::get('amocrm_refresh_token')),
'redirect_uri' => config('amocrm.redirect_uri'),
]);
$tokens = $resp->json();
Cache::put('amocrm_access_token', $tokens['access_token'], 82800); // 23 hours
Setting::set('amocrm_refresh_token', encrypt($tokens['refresh_token']));
return $tokens['access_token'];
}
}
UTM tags in request
Traffic source is fixed in cookies on first visit and sent with the form:
// Save UTM on first visit
const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
const params = new URLSearchParams(window.location.search);
utmParams.forEach(param => {
if (params.has(param)) {
document.cookie = `${param}=${params.get(param)};path=/;max-age=2592000`;
}
});
// Add UTM when submitting form
function getUtmData() {
return Object.fromEntries(
utmParams.map(p => [p, getCookie(p) || '']).filter(([, v]) => v)
);
}
Timeline
Request form with integration into one CRM via API: 3–5 working days.







